/*
 *	CHANGE LOG
 * 
 * 01.02.07
 *
 *		10/22/2008 - DW - Changed the code for videos so that the video player divs are included at the 
 *							End of the content and not in the middle.  Block level elements such as the 
 *							div shouldn't be included in the middle of inline elements such as the p tag.
 *		10/25/2008 - DW - Updated the NewMediaObject method to remove the : character from 
 *							a provider name.  This was needed because of Engage: Publish.
 *		10/25/2008 - DW - Updated the GetPage and GetPost code to always look at description, mt_text_more,
 *							and mt_exerpt, not just the description.
 *		11/04/2008 - DW - Added debug code to GetRecentPosts and GetPages
 * 
 *		11/23/2008 - DW - Fixed issues with the Tab and Footnote regex that limited users to only 10 items
 * 
 *  01.03.00
 *		
 *		01/11/2009 - DW - Changed the getPostCategories implementaiton.  It wasn't implemented.
 *
 *  01.03.02
 *		
 *		01/16/2009 - DW - Changed the getPostCategories implementaiton.  It was returning a null which causes an error.
 * 
 *  01.03.03
 *		
 *		01/19/2009 - DW - Changed all calls to GetString and GetStringGlobal to take a language parameter which is 
 *							currently set to the DefaultLanguage of the current portal.
 * 
 *		01/20/2009 - DW - Updated the Page Edit and Page Add procedures to include the call to the new infrastructure
 *							which configures redirection after publishing.
 *  01.03.05
 *		
 *		03/08/2009 - DW - Changed the code in GetStringGlobal to account for the fact that this method is called at times
 *							when we don't know anything about the portal yet (_portalSettings is null).
 * 
 *  01.03.05
 * 
 *		05/21/2009 - DW - Added a type=video attribute to the video lookup to allow for referencing videos without the swf extension.
 *							per Bruce Robinson's requests yesterday and today.
 *  01.03.06
 * 
 *		07/09/2009 - DW - Fixed an issue with /n added to the end of videos when editing existing content.
 *		                - Fixed an issue where the IDs related to videos were removed when editing existing content.
 *		                - Fixed an issue where font tags would throw off the tab feature.
 *		                - Added code to keep p and div tags from being added asymmetrically when the [tb] tags were added
 *		                    with the top tb tag on it's own line and the ending tb tag included inline (or vice versa).
 *		                    
 *  01.03.07
 *  
 *      11/??/2009 - DW - Made changes to metapostredirect.aspx to ensure that when users click on the View Blog link 
 *                          in WLW.
 *      11/11/2009 - DW - added {databaseOwner}{objectQualifier} to the fallback code in GetPortalIDFromAlias procedure.
 *      11/13/2009 - DW - removed the error logging from GetPortalIdFromAlias.
 *      11/30/2009 - DW - Closed the data readers when connections are made to the db. 
 *
 */


using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Reflection;
using System.Text.RegularExpressions;
using DotNetNuke.Data;
using DotNetNuke.Entities.Portals;
using DotNetNuke.Entities.Users;
using CookComputing.XmlRpc;
using DotNetNuke.Security.Membership;
using System.IO;
using System.Web;
using ITCrossing.Common;
using ITCrossing.Tracking;
using System.Runtime.InteropServices;

namespace ITCrossing
{
    # region MetaWebLog 

    /// <summary>
    /// Implements the MetaBlog API.
    /// </summary>
    public class metaPost3 : XmlRpcService, IMetaWebLog 
	{
		# region Private Variables
		
		private PortalSettings _portalSettings = null;
		private string _moduleName = string.Empty;
    	private string _expanderScript = string.Empty;
    	private UserInfo _userInfo = null;
    	private IPublishable _provider = null;
		
		# endregion

        public BlogInfoStruct[] getUsersBlogs(string appKey, string username, string password)
        {
			InitializeMethodCall(username, password);
			
			BlogInfoStruct[] infoArray;
        	
			try
			{
				string providerKey = (Context.Request["key"] != null
											&& Context.Request["key"].ToString() != string.Empty)
										? Context.Request["key"]
										: "Blog";
				ModuleInfoStruct[] misArray = _provider.GetModulesForUser(_userInfo, _portalSettings, providerKey);

				// Translate this to a BlogInfoStruct
				infoArray = new BlogInfoStruct[misArray.Length];
				for (int i = 0; i < misArray.Length; i++)
				{
					infoArray[i].blogid = misArray[i].ModuleLevelId;
					infoArray[i].blogName = misArray[i].ModuleName;
					infoArray[i].url = misArray[i].Url;
				}
			}
			catch (MetaPostException mex)
			{
				LogException(mex);
				if (mex.UseGlobal)
				{
					throw new XmlRpcFaultException(0, GetStringGlobal(mex.ResourceKey, mex.Message));
				}
				else
				{
					throw new XmlRpcFaultException(0, GetString(mex.ResourceKey, mex.Message));
				}
			}
			catch (XmlRpcFaultException ex)
			{
				LogException(ex);
				throw;
			}
			catch (Exception ex)
			{
				LogException(ex);
				string debugError = string.Empty;
				if (HttpContext.Current.Request != null && HttpContext.Current.Request["Debug"] != null)
				{
					debugError = "  Error message: " + ex.ToString();
				}
				throw new XmlRpcFaultException(0, GetString("GetUsersModulesError", "There was an error retrieving the list of the user's items.") + debugError); 
			}

			return infoArray;

		}

		# region Page Related Procedures
		
		public Post		getPage(string blog_id, string page_id, string username, string password)
		{
			Post post;
			InitializeMethodCall(username, password);
			try
			{
				post =
					getPostFromItem(_provider.GetItem(page_id.ToString(), _userInfo, _portalSettings, ItemType.Page));


				// Check to make sure we have a description.  If not and there's an entry for mt_text_more
				// copy this to description
				if (string.IsNullOrEmpty(post.description) && !string.IsNullOrEmpty(post.mt_text_more))
				{
					post.description = post.mt_text_more;
					post.mt_text_more = null;
				}

				// Remove the footer before sending back to client

				RemoveScript(ref post);

				// Look for and replace latest version of the string
				string regex = "<div[^i]*class=(\"|')*d_itc_f(\"|')*.+?</div>";
				RegexOptions options = RegexOptions.Singleline | RegexOptions.IgnoreCase;
				Regex replaceRegEx = new Regex(regex, options);
				if (!string.IsNullOrEmpty(post.description)) post.description =
					replaceRegEx.Replace(post.description, string.Empty);
				if (!string.IsNullOrEmpty(post.mt_text_more))
					post.mt_text_more = replaceRegEx.Replace(post.mt_text_more, string.Empty);
				if (!string.IsNullOrEmpty(post.mt_excerpt))
					post.mt_excerpt = replaceRegEx.Replace(post.mt_excerpt, string.Empty);


				// Look for and replace an earlier version of the string
				regex = "<div[^i]*id=(\"|')*d_itc_f(\"|')*.+?</div>";
				options = RegexOptions.Singleline;
				replaceRegEx = new Regex(regex, options);
				if (!string.IsNullOrEmpty(post.description)) post.description =
					replaceRegEx.Replace(post.description, string.Empty);
				if (!string.IsNullOrEmpty(post.mt_text_more)) post.mt_text_more =
					replaceRegEx.Replace(post.mt_text_more, string.Empty);
				if (!string.IsNullOrEmpty(post.mt_excerpt)) post.mt_excerpt =
					replaceRegEx.Replace(post.mt_excerpt, string.Empty);

				// Look for and replace 1.0.2 version of the string	
				regex = "<a[^i]*class=(\"|')*a_itc(\"|')*.+?</script>";
				replaceRegEx = new Regex(regex, options);
				if (!string.IsNullOrEmpty(post.description)) post.description =
					replaceRegEx.Replace(post.description, string.Empty);
				if (!string.IsNullOrEmpty(post.mt_text_more)) post.mt_text_more =
					replaceRegEx.Replace(post.mt_text_more, string.Empty);
				if (!string.IsNullOrEmpty(post.mt_excerpt)) post.mt_excerpt =
					replaceRegEx.Replace(post.mt_excerpt, string.Empty);

				// Look for and replace relative images with absolute images
				regex = "(src=\")/";
				if (!string.IsNullOrEmpty(post.mt_excerpt)) post.mt_excerpt =
						Regex.Replace(post.mt_excerpt, regex, "$1http://" + Context.Request.Url.Host + "/", options);
				if (!string.IsNullOrEmpty(post.description)) post.description =
						Regex.Replace(post.description, regex, "$1http://" + Context.Request.Url.Host + "/", options);
				if (!string.IsNullOrEmpty(post.mt_text_more)) post.mt_text_more =
						Regex.Replace(post.mt_text_more, regex, "$1http://" + Context.Request.Url.Host + "/", options);

				// Look for and replace Html Content Links
				regex = @"<!--Begin mp_html_link_(?<firstpart>\d*)(?:_\w{8})*--><\s*a[^>]+>(?<secondpart>.*)</a><sup.*<!--End mp_html_link_\1(?:_\w{8})*-->";
				if (!string.IsNullOrEmpty(post.description)) post.description =
					Regex.Replace(post.description, regex, "<a href=\"mp$1\">$2</a>", options);
				if (!string.IsNullOrEmpty(post.mt_text_more)) post.mt_text_more =
					Regex.Replace(post.mt_text_more, regex, "<a href=\"mp$1\">$2</a>", options);
				if (!string.IsNullOrEmpty(post.mt_excerpt)) post.mt_excerpt =
				 Regex.Replace(post.mt_excerpt, regex, "<a href=\"mp$1\">$2</a>", options);

				// Look for and replace Html Content Details
				regex = @"<!--Begin mp_html_detail_(?<firstpart>\d*)(?:_\w{8})*-->.*<!--Begin mp_html_detail_body_\1(?:_\w{8})*-->(?<secondpart>.*)<!--End mp_html_detail_body_\1(?:_\w{8})*-->.*<!--End mp_html_detail_\1(?:_\w{8})*-->";
				if (!string.IsNullOrEmpty(post.description)) post.description =
					Regex.Replace(post.description, regex, new MatchEvaluator(ContentHtmlReverseEvaluator), options);
				if (!string.IsNullOrEmpty(post.mt_text_more)) post.mt_text_more =
					Regex.Replace(post.mt_text_more, regex, new MatchEvaluator(ContentHtmlReverseEvaluator), options);
				if (!string.IsNullOrEmpty(post.mt_excerpt)) post.mt_excerpt =
					Regex.Replace(post.mt_excerpt, regex, new MatchEvaluator(ContentHtmlReverseEvaluator), options);

				// Look for and replace Tab Contents
				regex = @"<!--Begin mp_tabs_(?<firstpart>\w{8})-->(?<secondpart>.*)<!--End mp_tabs_\1-->";
				if (!string.IsNullOrEmpty(post.description)) post.description =
					Regex.Replace(post.description, regex, new MatchEvaluator(ContentTabsReverseEvaluator), options);
				if (!string.IsNullOrEmpty(post.mt_text_more)) post.mt_text_more =
					Regex.Replace(post.mt_text_more, regex, new MatchEvaluator(ContentTabsReverseEvaluator), options);
				if (!string.IsNullOrEmpty(post.mt_excerpt)) post.mt_excerpt =
					Regex.Replace(post.mt_excerpt, regex, new MatchEvaluator(ContentTabsReverseEvaluator), options);


				// Look for and replace Video Links
				regex = @"<!--Begin mp_video_(?<id>\w*)-->[^<]*"
					+ @"<!--(?<type>type=video)-->[^<]*"
					+ @"<!--url:(?<url>.*?)-->[^<]*"
					+ @"<!--width:(?<width>\d*)-->[^<]*"
					+ @"<!--height:(?<height>\d*)-->.*"
					+ @"<!--Begin mp_video_content-->(?<content>.*)<!--End mp_video_content-->.*"
					+ @"<!--End mp_video_\1-->";
				if (!string.IsNullOrEmpty(post.description)) post.description =
					Regex.Replace(post.description, regex, new MatchEvaluator(VideoReverseEvaluator), options);
				if (!string.IsNullOrEmpty(post.mt_text_more)) post.mt_text_more =
					Regex.Replace(post.mt_text_more, regex, new MatchEvaluator(VideoReverseEvaluator), options);
				if (!string.IsNullOrEmpty(post.mt_excerpt)) post.mt_excerpt =
					Regex.Replace(post.mt_excerpt, regex, new MatchEvaluator(VideoReverseEvaluator), options);	// Look for and replace Video Links
				// Look again for files that end in swf.
				regex = @"<!--Begin mp_video_(?<id>\w*)-->[^<]*"
					+ @"<!--url:(?<url>.*?)-->[^<]*"
					+ @"<!--width:(?<width>\d*)-->[^<]*"
					+ @"<!--height:(?<height>\d*)-->.*"
					+ @"<!--Begin mp_video_content-->(?<content>.*)<!--End mp_video_content-->.*"
					+ @"<!--End mp_video_\1-->";
				if (!string.IsNullOrEmpty(post.description)) post.description =
					Regex.Replace(post.description, regex, new MatchEvaluator(VideoReverseEvaluator), options);
				if (!string.IsNullOrEmpty(post.mt_text_more)) post.mt_text_more =
					Regex.Replace(post.mt_text_more, regex, new MatchEvaluator(VideoReverseEvaluator), options);
				if (!string.IsNullOrEmpty(post.mt_excerpt)) post.mt_excerpt =
					Regex.Replace(post.mt_excerpt, regex, new MatchEvaluator(VideoReverseEvaluator), options);


				// Look for and replace Video Viewers
				regex = @"<!--Begin itc_Video_Viewers-->.*<!--End itc_Video_Viewers-->";
				if (!string.IsNullOrEmpty(post.description)) post.description =
					Regex.Replace(post.description, regex, string.Empty, options);
				if (!string.IsNullOrEmpty(post.mt_text_more)) post.mt_text_more =
					Regex.Replace(post.mt_text_more, regex, string.Empty, options);
				if (!string.IsNullOrEmpty(post.mt_excerpt)) post.mt_excerpt =
					Regex.Replace(post.mt_excerpt, regex, string.Empty, options);
		

			}
			catch (MetaPostException ex)
			{
				LogException(ex);
				if (ex.UseGlobal)
				{
					throw new XmlRpcFaultException(0, GetStringGlobal(ex.ResourceKey, ex.Message));
				}
				else
				{
					throw new XmlRpcFaultException(0, GetString(ex.ResourceKey, ex.Message));
				}
			}
			catch (XmlRpcFaultException ex)
			{
				LogException(ex);
				throw;
			}
			catch (Exception ex)
			{
				LogException(ex);
				throw new XmlRpcFaultException(0, GetString("GetPageError", "There was an error retrieving this item."));
			}

			return post;

		}

		public Post[]	getPages(string blog_id, string username, string password, int numberOfPages)
		{
			Post[] posts;
			InitializeMethodCall(username, password);
			try
			{
				posts =
					getPostsFromItems(_provider.GetRecentItems(blog_id, _userInfo, _portalSettings,
					numberOfPages, RecentItemsRequestType.RecentPages, _provider.ProviderKey));
			}
			catch (MetaPostException ex)
			{
				LogException(ex);
				if (ex.UseGlobal)
				{
					throw new XmlRpcFaultException(0, GetStringGlobal(ex.ResourceKey, ex.Message));
				}
				else
				{
					throw new XmlRpcFaultException(0, GetString(ex.ResourceKey, ex.Message));
				}
			}
			catch (XmlRpcFaultException ex)
			{
				LogException(ex);
				throw;
			}
			catch (Exception ex)
			{
				LogException(ex);

				string debugError = string.Empty;
				if (HttpContext.Current.Request != null && HttpContext.Current.Request["Debug"] != null)
				{
					debugError = "  Error message: " + ex.ToString();
				}

				throw new XmlRpcFaultException(0, GetString("GetPagesError", "There was an error retrieving the list of items.") + debugError);
			}

			return posts;

		}

		public Page[]	getPageList(string blog_id, string username, string password)
		{
			Page[] pages;
			InitializeMethodCall(username, password);
			try
			{
				pages =
					getPagesFromItems(_provider.GetRecentItems(blog_id.ToString(), _userInfo,
						_portalSettings, -1, RecentItemsRequestType.RecentPagesHierarchical, _provider.ProviderKey));
			}
			catch (MetaPostException ex)
			{
				LogException(ex);
				if (ex.UseGlobal)
				{
					throw new XmlRpcFaultException(0, GetStringGlobal(ex.ResourceKey, ex.Message));
				}
				else
				{
					throw new XmlRpcFaultException(0, GetString(ex.ResourceKey, ex.Message));
				}
			}
			catch (XmlRpcFaultException ex)
			{
				LogException(ex);
				throw;
			}
			catch (Exception ex)
			{
				LogException(ex);
				throw new XmlRpcFaultException(0, GetString("GetPagesError", "There was an error retrieving the list of items."));
			}

			return pages;
		}

		public int		newPage(string blog_id, string username, string password, Post content, bool publish)
    	{
			int pageId = -1;
			InitializeMethodCall(username, password);
			try
			{

				MakeImagesRelative(ref content);
				RemoveScript(ref content);
				AddFooter(ref content);

				Item item = getItemFromPost(content);

				AddImageExpander(ref item);
				item.Publish = publish;
				item.StyleDetectionPost = false;
				item.StyleId = string.Empty;
				item.ItemType = ItemType.Page;

				string returnedPageId = 
					_provider.NewItem(	blog_id.ToString(), 
										_userInfo, 
										_portalSettings,
				                        item);
				pageId = Convert.ToInt32(returnedPageId);

				MetaPostServices.SetTempInstallUrl(_provider.ProviderKey, "DefaultBlogURL");

				if (_provider is ILinkable)
				{
					TrackbackAndPingSettings taps =
						((ILinkable)_provider).GetPingbackSettings(blog_id, _userInfo, _portalSettings);

					bool autoDiscovery = (taps.AllowOverrideByPingDropDown)
					                     	? (content.mt_allow_pings == 1)
					                     	: taps.AllowAutoDiscovery;

					if (taps.AllowForPage)
						HandleTrackbacksOrPings(blog_id, pageId.ToString(), ref content, publish, (ILinkable)_provider,
						                        autoDiscovery);
				}
			}
			catch (MetaPostException ex)
			{
				LogException(ex);
				if (ex.UseGlobal)
				{
					throw new XmlRpcFaultException(0, GetStringGlobal(ex.ResourceKey, ex.Message));
				}
				else
				{
					throw new XmlRpcFaultException(0, GetString(ex.ResourceKey, ex.Message));
				}
			}
			catch (XmlRpcFaultException ex)
			{
				LogException(ex);
				throw;
			}
			catch (Exception ex)
			{
				LogException(ex); 
				throw new XmlRpcFaultException(0, GetString("CreateError", "There was an error adding this new item."));
			}

			return pageId;

    	}

    	public bool		editPage(string blog_id, string page_id, string username, string password, Post content, bool publish)
    	{
			bool success = false;
			InitializeMethodCall(username, password);
			try
			{

				// Check to see if the footer has been added.
				if (content.description.IndexOf("d_itc_f") <= 0)
				{
					// Retrieve the TabID where this blog can be viewed
					MakeImagesRelative(ref content);
					RemoveScript(ref content);
					AddFooter(ref content);
				}

				Item item = getItemFromPost(content);

				AddImageExpander(ref item);

				item.ItemId = page_id;
				item.Publish = publish;
				item.ItemType = ItemType.Page;
				success =
					(bool)_provider.EditItem(blog_id, _userInfo, _portalSettings, item);

				MetaPostServices.SetTempInstallUrl(_provider.ProviderKey, "DefaultBlogURL");
			}
			catch (MetaPostException ex)
			{
				LogException(ex);
				if (ex.UseGlobal)
				{
					throw new XmlRpcFaultException(0, GetStringGlobal(ex.ResourceKey, ex.Message));
				}
				else
				{
					throw new XmlRpcFaultException(0, GetString(ex.ResourceKey, ex.Message));
				}
			}
			catch (XmlRpcFaultException ex)
			{
				LogException(ex);
				throw;
			}
			catch (Exception ex)
			{
				LogException(ex);
				throw new XmlRpcFaultException(0, GetString("EditError", "There was an error editing this item."));
			}
			return success;
    	}

    	public bool		deletePage(string blog_id, string username, string password, string page_id)
    	{
			bool success = false;
			InitializeMethodCall(username, password);
			try
			{
				success =
					(bool)_provider.DeleteItem(page_id, _userInfo, _portalSettings, ItemType.Page);
			}
			catch (MetaPostException ex)
			{
				LogException(ex);
				if (ex.UseGlobal)
				{
					throw new XmlRpcFaultException(0, GetStringGlobal(ex.ResourceKey, ex.Message));
				}
				else
				{
					throw new XmlRpcFaultException(0, GetString(ex.ResourceKey, ex.Message));
				}
			}
			catch (XmlRpcFaultException ex)
			{
				LogException(ex);
				throw;
			}
			catch (Exception ex)
			{
				LogException(ex); 
				throw new XmlRpcFaultException(0, GetString("DeletePageError", "There was an error deleting this item."));
			}
			return success;
		}

    	# endregion

		# region Post Related Procedures

		public Post		getPost(string postid, string username, string password)
		{
			Post post;
			InitializeMethodCall(username, password);
			try
			{
				post =
					getPostFromItem(_provider.GetItem(postid.ToString(), _userInfo, _portalSettings, ItemType.Post));


				// Check to make sure we have a description.  If not and there's an entry for mt_text_more
				// copy this to description
				if (string.IsNullOrEmpty(post.description) && !string.IsNullOrEmpty(post.mt_text_more))
				{
					post.description = post.mt_text_more;
					post.mt_text_more = null;
				}

				// Remove the footer before sending back to client
				// This is only important for those using teh community edition.

				RemoveScript(ref post);

				// Look for and replace latest version of the string
				string regex = "<div[^i]*class=(\"|')*d_itc_f(\"|')*.+?</div>";
				RegexOptions options = RegexOptions.Singleline | RegexOptions.IgnoreCase;
				Regex replaceRegEx = new Regex(regex, options);
				if (!string.IsNullOrEmpty(post.description)) post.description =
					replaceRegEx.Replace(post.description, string.Empty);
				if (!string.IsNullOrEmpty(post.mt_text_more)) post.mt_text_more =
					replaceRegEx.Replace(post.mt_text_more, string.Empty);
				if (!string.IsNullOrEmpty(post.mt_excerpt)) post.mt_excerpt =
					replaceRegEx.Replace(post.mt_excerpt, string.Empty);

				// Look for and replace an earlier version of the string
				regex = "<div[^i]*id=(\"|')*d_itc_f(\"|')*.+?</div>";
				options = RegexOptions.Singleline;
				replaceRegEx = new Regex(regex, options);
				if (!string.IsNullOrEmpty(post.description)) post.description =
					replaceRegEx.Replace(post.description, string.Empty);
				if (!string.IsNullOrEmpty(post.mt_text_more)) post.mt_text_more =
					replaceRegEx.Replace(post.mt_text_more, string.Empty);
				if (!string.IsNullOrEmpty(post.mt_excerpt)) post.mt_excerpt =
					replaceRegEx.Replace(post.mt_excerpt, string.Empty);

				// Look for and replace 1.0.2 version of the string	
				regex = "<a[^i]*class=(\"|')*a_itc(\"|')*.+?</script>";
				replaceRegEx = new Regex(regex, options);
				if (!string.IsNullOrEmpty(post.description)) post.description =
					replaceRegEx.Replace(post.description, string.Empty);
				if (!string.IsNullOrEmpty(post.mt_text_more)) post.mt_text_more =
					replaceRegEx.Replace(post.mt_text_more, string.Empty);
				if (!string.IsNullOrEmpty(post.mt_excerpt)) post.mt_excerpt =
					replaceRegEx.Replace(post.mt_excerpt, string.Empty);

				// Look for and replace relative images with absolute images
				regex = "(src=\")/";
				if (!string.IsNullOrEmpty(post.mt_excerpt)) post.mt_excerpt =
					Regex.Replace(post.mt_excerpt, regex, "$1http://" + Context.Request.Url.Host + "/", options);
				if (!string.IsNullOrEmpty(post.description)) post.description =
					Regex.Replace(post.description, regex, "$1http://" + Context.Request.Url.Host + "/", options);
				if (!string.IsNullOrEmpty(post.mt_text_more)) post.mt_text_more =
					Regex.Replace(post.mt_text_more, regex, "$1http://" + Context.Request.Url.Host + "/", options);

				// Look for and replace Html Content Links
				regex = @"<!--Begin mp_html_link_(?<firstpart>\d*)(?:_\w{8})*--><\s*a[^>]+>(?<secondpart>.*)</a><sup.*<!--End mp_html_link_\1(?:_\w{8})*-->";
				if (!string.IsNullOrEmpty(post.description)) post.description =
					Regex.Replace(post.description, regex, "<a href=\"mp$1\">$2</a>", options);
				if (!string.IsNullOrEmpty(post.mt_text_more)) post.mt_text_more =
					Regex.Replace(post.mt_text_more, regex, "<a href=\"mp$1\">$2</a>", options);
				if (!string.IsNullOrEmpty(post.mt_excerpt)) post.mt_excerpt =
					Regex.Replace(post.mt_excerpt, regex, "<a href=\"mp$1\">$2</a>", options);

				// Look for and replace Html Content Details
				regex = @"<!--Begin mp_html_detail_(?<firstpart>\d*)(?:_\w{8})*-->.*<!--Begin mp_html_detail_body_\1(?:_\w{8})*-->(?<secondpart>.*)<!--End mp_html_detail_body_\1(?:_\w{8})*-->.*<!--End mp_html_detail_\1(?:_\w{8})*-->";
				if (!string.IsNullOrEmpty(post.description)) post.description =
					Regex.Replace(post.description, regex, new MatchEvaluator(ContentHtmlReverseEvaluator), options);
				if (!string.IsNullOrEmpty(post.mt_text_more)) post.mt_text_more =
					Regex.Replace(post.mt_text_more, regex, new MatchEvaluator(ContentHtmlReverseEvaluator), options);
				if (!string.IsNullOrEmpty(post.mt_excerpt)) post.mt_excerpt =
					Regex.Replace(post.mt_excerpt, regex, new MatchEvaluator(ContentHtmlReverseEvaluator), options);

				// Look for and replace Tab Contents
				regex = @"<!--Begin mp_tabs_(?<firstpart>\w{8})-->(?<secondpart>.*)<!--End mp_tabs_\1-->";
				if (!string.IsNullOrEmpty(post.description)) post.description =
					Regex.Replace(post.description, regex, new MatchEvaluator(ContentTabsReverseEvaluator), options);
				if (!string.IsNullOrEmpty(post.mt_text_more)) post.mt_text_more =
					Regex.Replace(post.mt_text_more, regex, new MatchEvaluator(ContentTabsReverseEvaluator), options);
				if (!string.IsNullOrEmpty(post.mt_excerpt)) post.mt_excerpt =
					Regex.Replace(post.mt_excerpt, regex, new MatchEvaluator(ContentTabsReverseEvaluator), options);

				// Look for and replace Video Links
				regex = @"<!--Begin mp_video_(?<id>\w*)-->[^<]*"
					+ @"<!--url:(?<url>.*?)-->[^<]*"
					+ @"<!--width:(?<width>\d*)-->[^<]*"
					+ @"<!--height:(?<height>\d*)-->.*"
					+ @"<!--Begin mp_video_content-->(?<content>.*)<!--End mp_video_content-->.*"
					+ @"<!--End mp_video_\1-->";
				if (!string.IsNullOrEmpty(post.description)) post.description =
					Regex.Replace(post.description, regex, new MatchEvaluator(VideoReverseEvaluator), options);
				if (!string.IsNullOrEmpty(post.mt_text_more)) post.mt_text_more =
					Regex.Replace(post.mt_text_more, regex, new MatchEvaluator(VideoReverseEvaluator), options);
				if (!string.IsNullOrEmpty(post.mt_excerpt)) post.mt_excerpt =
					Regex.Replace(post.mt_excerpt, regex, new MatchEvaluator(VideoReverseEvaluator), options);

				// Look for and replace Video Viewers
				regex = @"<!--Begin itc_Video_Viewers-->.*<!--End itc_Video_Viewers-->";
				if (!string.IsNullOrEmpty(post.description)) post.description =
					Regex.Replace(post.description, regex, string.Empty, options);
				if (!string.IsNullOrEmpty(post.mt_text_more)) post.mt_text_more =
					Regex.Replace(post.mt_text_more, regex, string.Empty, options);
				if (!string.IsNullOrEmpty(post.mt_excerpt)) post.mt_excerpt =
					Regex.Replace(post.mt_excerpt, regex, string.Empty, options);

			}
			catch (MetaPostException ex)
			{
				LogException(ex);
				if (ex.UseGlobal)
				{
					throw new XmlRpcFaultException(0, GetStringGlobal(ex.ResourceKey, ex.Message));
				}
				else
				{
					throw new XmlRpcFaultException(0, GetString(ex.ResourceKey, ex.Message));
				}
			}
			catch (XmlRpcFaultException ex)
			{
				LogException(ex);
				throw;
			}
			catch (Exception ex)
			{
				LogException(ex);
				throw new XmlRpcFaultException(0, GetString("GetPostError", "There was an error retrieving this item."));
			}

			return post;
		}

		public Post[]	getRecentPosts(string blogid, string username, string password, int numberOfPosts)
		{
			Post[] posts;
			InitializeMethodCall(username, password);
			try
			{
				posts =
					getPostsFromItems(_provider.GetRecentItems(blogid.ToString(), _userInfo, _portalSettings,
						numberOfPosts, RecentItemsRequestType.RecentPosts, _provider.ProviderKey));
			}
			catch (MetaPostException ex)
			{
				LogException(ex);
				if (ex.UseGlobal)
				{
					throw new XmlRpcFaultException(0, GetStringGlobal(ex.ResourceKey, ex.Message));
				}
				else
				{
					throw new XmlRpcFaultException(0, GetString(ex.ResourceKey, ex.Message));
				}
			}
			catch (XmlRpcFaultException ex)
			{
				LogException(ex);
				throw;
			}
			catch (Exception ex)
			{
				LogException(ex);

				string debugError = string.Empty;
				if (HttpContext.Current.Request != null && HttpContext.Current.Request["Debug"] != null)
				{
					debugError = "  Error message: " + ex.ToString();
				}
				throw new XmlRpcFaultException(0, GetString("RecentPostsError", "There was an error retrieving the list of items.") + debugError);
			}

			return posts;
		}

		/// <summary>
		/// Creates a new post.  The publish boolean is used to determine whether the item 
		/// should be published or not.
		/// </summary>
		/// <param name="blogId">The blogid.</param>
		/// <param name="username">The username.</param>
		/// <param name="password">The password.</param>
		/// <param name="post">The post.</param>
		/// <param name="publish">if set to <c>true</c> [publish].</param>
		/// <returns></returns>
		public string	newPost(string blogId, string username, string password, Post post, bool publish)
		{
			InitializeMethodCall(username, password);

			string pageId = string.Empty;
			string styleId = string.Empty;
			bool styleDetectionPost = false;

			try
			{

				
				// Check to see if this a style detection post.
				styleDetectionPost = IsStyleDetectionPost(post);

				// Check to see if a styleId is passed through the QueryString
				// however, we'll only do this if we are creating a post for style detection.
				if (styleDetectionPost)
				{
					if (Context.Request["sid"] != null && !string.IsNullOrEmpty(Context.Request["sid"]))
					{
						styleId = Context.Request["sid"];
					}
				}
				else
				{
					MakeImagesRelative(ref post);
					RemoveScript(ref post);
					// If not styleDetectionPost, then we need to add the footer.
					// Use reflection to see if the provider supports a GetSummaryMaxLength
					MethodInfo mi = _provider.GetType().GetMethod("GetSummaryMaxLength");
					if (mi != null)
					{
						int MaxLength = (int)mi.Invoke(_provider, new object[] {blogId, _userInfo, _portalSettings});
						AddFooter(ref post, MaxLength);
					}
					else
					{
						AddFooter(ref post);	
					}
					
				}

				Item item = getItemFromPost(post);

				// For the professional edition, we'll add the expander effect.
				AddImageExpander(ref item);
				item.Publish = publish;
				item.StyleDetectionPost = styleDetectionPost;
				item.StyleId = styleId;
				item.ItemType = ItemType.Post;

				pageId = _provider.NewItem(blogId.ToString(), _userInfo, _portalSettings, item);

				MetaPostServices.SetTempInstallUrl(_provider.ProviderKey, "DefaultBlogURL");

				if (_provider is ILinkable)
				{
					TrackbackAndPingSettings taps =
						((ILinkable) _provider).GetPingbackSettings(blogId, _userInfo, _portalSettings);
					bool autoDiscovery = (taps.AllowOverrideByPingDropDown)
					                     	? (post.mt_allow_pings == 1)
					                     	: taps.AllowAutoDiscovery;
					if (taps.AllowForPost)
						HandleTrackbacksOrPings(blogId, pageId, ref post, publish, (ILinkable)_provider, autoDiscovery);
				}

				# region Code to Help WLW Retrieve Site Style Information

				if (styleDetectionPost)
				{
					// Check first to see if WLW is trying to retrieve a post for identifying the 
					// template.  
					// Retrieve the upload URL from the modules settings and overwrite what was passed in
					// for the intendedUrl.
					string sSQL =
						"SELECT TempInstallUrl FROM {databaseOwner}{objectQualifier}itcMetaPost (nolock) WHERE ItemId = 1";
					string sURL = null;
                    using (IDataReader dr = DataProvider.Instance().ExecuteSQL(sSQL))
                    {
                        while (dr.Read())
                        {
                            sURL = dr["TempInstallUrl"].ToString() + "";
                        }
                    }

					// If the TempInstallUrl isn't set, then we will set it with a default string of 
					// 'IntendedUrl' to note that we should redirect directly to the IntendedUrl passed
					// in through the QueryString.
					if (string.IsNullOrEmpty(sURL))
					{
						sSQL =
							"UPDATE {databaseOwner}{objectQualifier}itcMetaPost SET TempInstallUrl = 'IntendedUrl' WHERE ItemId = 1";
						DataProvider.Instance().ExecuteSQL(sSQL);
					}
				}

				# endregion

			}
			catch (MetaPostException ex)
			{
				LogException(ex);
				if (ex.UseGlobal)
				{
					throw new XmlRpcFaultException(0, GetStringGlobal(ex.ResourceKey, ex.Message));	
				}
				else
				{
					throw new XmlRpcFaultException(0, GetString(ex.ResourceKey, ex.Message));
				}
			}
			catch (XmlRpcFaultException ex)
			{
				LogException(ex);
				throw;
			}
			catch (Exception ex)
			{
				LogException(ex);
				throw new XmlRpcFaultException(0, GetString("CreateError", "There was an error adding this new item.")); // + "Error: " + ex.ToString()
			}

			return pageId;
		}

    	public bool		editPost(string postid, string username, string password, Post post, bool publish)
		{
			bool success = false;
			InitializeMethodCall(username, password);
			try
			{
				// Check to see if the footer has been added. 
				if (post.description.IndexOf("d_itc_f") <= 0)
				{

                    // Exclude the Axon provider (Yes, this should definitely be refactored).
                    if (_provider.ManifestFilePath != "/DesktopModules/itcMetaPost/manifests/wlwaxon.xml")
                    {
                        MakeImagesRelative(ref post);
                        RemoveScript(ref post);

                        // Retrieve the TabID where this blog can be viewed
                        // Use reflection to see if the provider supports a GetSummaryMaxLength
                        MethodInfo miModId = _provider.GetType().GetMethod("GetModuleIdFromItemId");
                        MethodInfo miMaxLen = _provider.GetType().GetMethod("GetSummaryMaxLength");
                        if (miModId != null && miMaxLen != null)
                        {
                            string blogId =
                                (string)miModId.Invoke(_provider, new object[] { postid, _portalSettings.PortalId });
                            int MaxLength = (int)miMaxLen.Invoke(_provider, new object[] { blogId, _userInfo, _portalSettings });
                            AddFooter(ref post, MaxLength);
                        }
                        else
                        {
                            AddFooter(ref post);
                        }
                    }
				}

				Item item = getItemFromPost(post);

				// Add the expander effect for the professional edition
                // Exclude the Axon provider (Yes, this should definitely be refactored).
                if (_provider.ManifestFilePath != "/DesktopModules/itcMetaPost/manifests/wlwaxon.xml")
    				AddImageExpander(ref item);

				item.ItemId = postid;
				item.Publish = publish;
				item.ItemType = ItemType.Post;

				success =
					(bool)_provider.EditItem(null, _userInfo, _portalSettings, item);

				MetaPostServices.SetTempInstallUrl(_provider.ProviderKey, "DefaultBlogURL");

			}
			catch (MetaPostException ex)
			{
				LogException(ex);
				throw new XmlRpcFaultException(0, GetString(ex.ResourceKey, ex.Message));
			}
			catch (XmlRpcFaultException ex)
			{
				LogException(ex);
				throw;
			}
			catch (Exception ex)
			{
				LogException(ex); 
				throw new XmlRpcFaultException(0, GetString("EditError", "There was an error editing this item."));
			}
			return success;
		}

		public bool		deletePost(string appKey, string postid, string username, string password, [XmlRpcParameter(Description = "Where applicable, this specifies whether the blog should be republished after the post has been deleted.")] bool publish)
        {
			bool success = false;
			InitializeMethodCall(username, password);
			try
			{
				success =
					(bool)_provider.DeleteItem(postid.ToString(), _userInfo, _portalSettings, ItemType.Post);
			}
			catch (MetaPostException ex)
			{
				LogException(ex);
				throw new XmlRpcFaultException(0, GetString(ex.ResourceKey, ex.Message));
			}
			catch (XmlRpcFaultException ex)
			{
				LogException(ex);
				throw;
			}
			catch (Exception ex)
			{
				LogException(ex);
				throw new XmlRpcFaultException(0, GetString("DeletePostError", "There was an error deleting this item."));
			}
			return success;
		}

		# endregion 

		# region Category Related Procedures
		public CategoryInfo[] getWPCategories(string blog_id, string username, string password)
		{
			CategoryInfo[] categories;
			InitializeMethodCall(username, password);
			try
			{
				categories =
					getCategoryInfosFromItemCategoryInfos(_provider.GetCategories(blog_id.ToString(), _userInfo, _portalSettings));
			}
			catch (MetaPostException ex)
			{
				LogException(ex);
				throw new XmlRpcFaultException(0, GetString(ex.ResourceKey, ex.Message));
			}
			catch (XmlRpcFaultException ex)
			{
				LogException(ex);
				throw;
			}
			catch (Exception ex)
			{
				LogException(ex);
				string debugError = string.Empty;
				if (HttpContext.Current.Request != null && HttpContext.Current.Request["Debug"] != null)
				{
					debugError = "  Error message: " + ex.ToString();
				}
				throw new XmlRpcFaultException(0, GetString("GetCategoriesError", "There was an error retrieving the list of items.") + debugError);
			}
			// Check to make sure we're not returning null
			if (categories == null)
			{
				categories = new CategoryInfo[0] {};
			}

			return categories;
		}

		public bool setPostCategories(string postid, string username, string password,
										PostedCategory[] categories)
		{
			InitializeMethodCall(username, password);
			try
			{
				return _provider.SetItemCategories(postid, GetItemCategoriesFromPostedCategories(categories), _userInfo, _portalSettings);
			}
			catch (MetaPostException ex)
			{
				LogException(ex);
				throw new XmlRpcFaultException(0, GetString(ex.ResourceKey, ex.Message));
			}
			catch (XmlRpcFaultException ex)
			{
				LogException(ex); 
				throw;
			}
			catch (Exception ex)
			{
				LogException(ex);
				throw new XmlRpcFaultException(0, GetString("SetCategoriesError", "There was an error setting the categories for this item."));
			}
		}

    	public PostedCategory[] getPostCategories(string postid, string username, string password)
    	{
			InitializeMethodCall(username, password);
			try
			{
				ItemCategory[] itemCategories = _provider.GetItemCategories(postid, _userInfo, _portalSettings);

				if (itemCategories == null)
				{
					return new PostedCategory[0];
				}
				else
				{
					return GetPostedCategoriesFromItemCategories(itemCategories);					
				}

			}
			catch (MetaPostException ex)
			{
				LogException(ex);
				throw new XmlRpcFaultException(0, GetString(ex.ResourceKey, ex.Message));
			}
			catch (XmlRpcFaultException ex)
			{
				LogException(ex);
				throw;
			}
			catch (Exception ex)
			{
				LogException(ex);
				throw new XmlRpcFaultException(0, GetString("GetCategoriesError", "There was an error retrieving the categories for this item."));
			}
    		
    	}

    	public int newCategory(string blog_id, string username, string password, NewCategory category)
		{
			int categoryId = -1;
			InitializeMethodCall(username, password);
			try
			{
				// Create a new category we can send to the provider
				NewItemCategory nic = new NewItemCategory();
				nic.ParentId = category.parent_id;
				nic.Slug = category.slug;
				nic.Name = category.name;
				nic.Description = category.description;

				categoryId = _provider.NewCategory(blog_id, nic, _userInfo, _portalSettings);
			}
			catch (MetaPostException ex)
			{
				LogException(ex);
				throw new XmlRpcFaultException(0, GetString(ex.ResourceKey, ex.Message));
			}
			catch (XmlRpcFaultException ex)
			{
				LogException(ex);
				throw;
			}
			catch (Exception ex)
			{
				LogException(ex);
				throw new XmlRpcFaultException(0, GetString("NewCategoryError", "There was an error adding this new item."));
			}

			return categoryId;

		}

		public MetaWebLogCategoryInfo[] getCategories(string blogid, string username, string password)
        {

			return getMetaWebLogCategoryInfosFromCateogryInfos(getWPCategories(blogid, username, password));

		}
		

		# endregion

		public mediaObjectInfo newMediaObject(object blogid, string username, string password, mediaObject mediaobject)
        {
			mediaObjectInfo info ;

			try
			{
				InitializeMethodCall(username, password);

				// Authorize the user
				MetaPostServices.AuthorizeUser(AuthorizationType.ModuleLevelId, (string) blogid,
				                               _provider.GetModulesForUser(_userInfo, _portalSettings,
				                                                           _provider.ProviderKey));
				string virtualPath;
				string mediaObjectName = string.Empty;
				string mediaObjectNameUrl = string.Empty;
				string fullFilePathAndName = string.Empty;
				string allowedUploadExtensions = string.Empty;
				info.url = "";

				try
				{
					// Retrieve the ImageUploadPath
					virtualPath = _provider.ImageUploadPath.ToLower();
					// If nothing is returned, then default to images/modulename/
					Hashtable metaPostSettings = getmetaPostSettings();
					if (string.IsNullOrEmpty(virtualPath)) virtualPath = metaPostSettings["URL"].ToString().ToLower();

					// Make sure we are uploading a valid extension
					allowedUploadExtensions = metaPostSettings["AllowedUploadExtensions"].ToString().ToLower();
					string extension = mediaobject.name.Substring(mediaobject.name.LastIndexOf(".") + 1).ToLower();
					if (allowedUploadExtensions.IndexOf(extension) < 0)
					{
						// Throw an exception since we shouldn't be able to upload an image type that's not allowed.
						throw new XmlRpcFaultException(0, GetStringGlobal("ImageUploadExtensionError", "One of the images or files attached to this post contains an unsupported file extension.  Please attach files with the following extensions: " + allowedUploadExtensions));
					}

					// Make sure we don't have a beginning forward slash.
					if (!string.IsNullOrEmpty(virtualPath) && virtualPath.Substring(0, 1) == "/") virtualPath = virtualPath.Substring(1);

					// Shorten WindowsLiveWriter
	 				mediaObjectName = mediaobject.name.Replace("WindowsLiveWriter", "WLW");
					mediaObjectNameUrl = mediaObjectName;

					// Check to make sure the FolderFilePath or FlattenedFilePath have been included
					if (!virtualPath.Contains("[folderfilepath]") && !virtualPath.Contains("[flattenedfilepath]"))
					{
						// We'll add [FolderFilePath] to the end
						if (!virtualPath.EndsWith("/")) virtualPath += "/";
						virtualPath += "[folderfilepath]";
					}

					// Replace Tokens in the image path
					virtualPath = virtualPath.Replace("[user]", _userInfo.Username);
					virtualPath = virtualPath.Replace("[provider]", _provider.ProviderKey.Replace(" ","-").Replace(":", string.Empty));
					virtualPath = virtualPath.Replace("[year]", DateTime.Now.Year.ToString());
					virtualPath = virtualPath.Replace("[month]", DateTime.Now.ToString("MMM"));
					virtualPath = virtualPath.Replace("[day]", DateTime.Now.ToString("ddd"));
					virtualPath = virtualPath.Replace("[flattenedfilepath]", mediaObjectName.Replace("/","-"));
					virtualPath = virtualPath.Replace("[folderfilepath]", mediaObjectName);

					fullFilePathAndName = _portalSettings.HomeDirectoryMapPath
					                      + virtualPath.Replace("/", @"\");

					//_provider.BeforeMediaSaved(fullFilePathAndName);

					FileStream output =
						new FileStream(CreateFoldersForFilePath(fullFilePathAndName), FileMode.Create);
					new BinaryWriter(output).Write(mediaobject.bits);
					output.Close();
				}
				catch (IOException exc)
				{
					throw new XmlRpcFaultException(0,
					                               GetString("ImageSaveError",
					                                         "An error occurred while saving an image related to this blog post."));
				}
				//string finalUrl = Context.Request.Url.Scheme + "://" + Context.Request.Url.Host;
				//finalUrl += _portalSettings.HomeDirectory.Replace(@"\", "/");
				string finalUrl = _portalSettings.HomeDirectory.Replace(@"\", "/");

				finalUrl += "_itc_new_image_/" + virtualPath;

				info.url = finalUrl;
			}
			catch (MetaPostException ex)
			{
				LogException(ex);
				if (ex.UseGlobal)
				{
					throw new XmlRpcFaultException(0, GetStringGlobal(ex.ResourceKey, ex.Message));
				}
				else
				{
					throw new XmlRpcFaultException(0, GetString(ex.ResourceKey, ex.Message));
				}
			}
			catch (XmlRpcFaultException ex)
			{
				LogException(ex);
				throw;
			}
			catch (Exception ex)
			{
				LogException(ex);
				throw new XmlRpcFaultException(0, GetString("ImageSaveError", "An error occurred while saving an image related to this blog post."));
			}
			return info;

        }

    	# region Private Methods

		private UserInfo ValidateUser(string username, string password, string ipAddress)
		{
			UserInfo userInfo = null;
			try
			{

				UserLoginStatus loginStatus = UserLoginStatus.LOGIN_FAILURE;
				userInfo =
					UserController.ValidateUser(_portalSettings.PortalId, username, password, "",
												_portalSettings.PortalName,
												ipAddress, ref loginStatus);

				if (userInfo.Roles == null)
				{
					// This fix was added to address an issue with DNN 04.05.05
					userInfo.Roles = GetRolesByUser(userInfo.UserID, _portalSettings.PortalId);
				}

				if (loginStatus != UserLoginStatus.LOGIN_SUCCESS 
                    && loginStatus != UserLoginStatus.LOGIN_SUPERUSER
                    && loginStatus.ToString("g") != "LOGIN_INSECUREADMINPASSWORD"
                    && loginStatus.ToString("g") != "LOGIN_INSECUREHOSTPASSWORD")
				{

					throw new XmlRpcFaultException(0, GetString("UserInvalid", "Either the username or the password is invalid. "));
					// testing code that can be added to the end of the previous string.
					// PortalId:" + _portalSettings.PortalId + " PortalName:" + _portalSettings.PortalName + " portalAlias:" + portalAlias
				}
			}
			catch (XmlRpcFaultException)
			{
				throw;
			}
			catch (Exception ex)
			{
				throw new XmlRpcFaultException(0, GetStringGlobal("AuthenticationError", "An error occurred while trying to authenticate the user. "));
			}

			return userInfo;
		}

    	private string[] GetRolesByUser(int userId, int portalId)
    	{
    		string[] userRoles = null;
			List<string> roles = new List<string>();

    		string SQL = "GetRolesByUser";
    		object[] methodParams = {userId, portalId};
            using (IDataReader dr = DataProvider.Instance().ExecuteReader(SQL, methodParams))
            {
                while (dr.Read())
                {
                    roles.Add(dr["RoleName"].ToString());
                }
            }

    		userRoles = roles.ToArray();

    		return userRoles;
    	}

    	private void GetPortalSettings()
		{
    		string portalAlias = string.Empty;
			try
			{
				HttpRequest Request = this.Context.Request;

				// Set the value of the _expanderScript
				string appPath = MetaPostServices.GetAppPath();
				_expanderScript = "<script type=\"text/javascript\" src=\"" + appPath + "/DesktopModules/itcMetaPost/js/ca0c21fbdc85f6a1597417732d450607.ashx\"></script>";
			
				// Check to see which key was passed in through the QueryString.  If no key, then
				// we assume it's the blog module being requested.
				if (Request["key"] != null && Request["key"].ToString() != string.Empty)
				{
					_moduleName = Request["key"].ToString();
				}
				else
				{
					_moduleName = "Blog";
				}

				// Parse the contents of the calias passed through the query string to retrieve the 
				// actual portal alias.
				if (Request.QueryString["calias"] != null)
				{
					portalAlias = Request.QueryString["calias"].Replace(Request.Url.Scheme, "");
					portalAlias = portalAlias.Replace("://", "");
				}
				else
				{
					string url = Request.Url.ToString().ToLower();
					portalAlias = url.Substring(0,url.IndexOf("/metapost.ashx"));
					portalAlias = portalAlias.Replace(Request.Url.Scheme, string.Empty).Replace("://", string.Empty);
					if (!string.IsNullOrEmpty(appPath)) portalAlias = portalAlias.Replace(appPath, string.Empty);
				}

				# region The old way - left for reference.
				//Request.CurrentExecutionFilePath.ToLower().Replace("/metapost.ashx", "");
				//if (Context.Request.QueryString["calias"] != null &&
				//    !string.IsNullOrEmpty(Context.Request.QueryString["calias"].ToString()))
				//{
				//    portalAlias += "/" + Context.Request.QueryString["calias"].ToString();
				//}
				# endregion

				int portalID = -1;
				portalID = GetPortalIDFromAlias(portalAlias);

                if (Context.Request["debug"] != null)
                {
                    Exception testException = new Exception("PortalID: " + portalID.ToString() + " PortalAlias:" + portalAlias);
                    LogException(testException);
                    //throw new XmlRpcFaultException(0, "PortalID: " + portalID.ToString() + " PortalAlias:" + portalAlias);
                }

				if (portalID == -1)
				{
					// Check to see if we don't have a portalID because the alias used in DotNetNuke does not 
					// have the www. in it.
					portalAlias = portalAlias.Replace("www.", string.Empty);
					portalID = GetPortalIDFromAlias(portalAlias);
				}

				// if we don't have teh portalID at this point, then we are dealing with a test instance of 
				// DNN using a URL in this format:  http://hostname/dnninstance.  In this case, the portal alias 
				// is hostname/dnninstance, so we'll give that a spin.

				if (portalID == -1)
				{
					if (Request.QueryString["calias"] != null &&
						!string.IsNullOrEmpty(Request.QueryString["calias"].ToString()))
					{
						portalAlias = Request.Url.Host + "/" + Request.QueryString["calias"].ToString();
						portalID = GetPortalIDFromAlias(portalAlias);
					}
				}

				// Check to see if we have access to post to this site:
				CheckForPortalRights(portalID);

				PortalAliasController pac = new PortalAliasController();
				PortalAliasCollection paColl = pac.GetPortalAliasByPortalID(portalID);

				PortalAliasInfo pai = null;

				foreach(string paiKey in paColl.Keys)
				{
					if(paColl[paiKey].HTTPAlias.ToLower() == portalAlias.ToLower())
					{
						pai = paColl[paiKey];
					}
				}

				PortalController portalController = new PortalController();
				PortalInfo pi = portalController.GetPortal(portalID);

				_portalSettings = new PortalSettings(pi.HomeTabId, pai);
			}
			catch (XmlRpcFaultException ex)
			{
				throw;
			}
			catch (Exception exc)
			{
				string calias = Context.Request.QueryString["calias"] as string;
				if (calias == null) calias = string.Empty;
				if (Context.Request["debug"] != null)
				{
					throw new XmlRpcFaultException(0, GetStringGlobal("PortalLoadError", "We tried everything, but we weren't able to identify a valid DotNetNuke portal based on the URL provided.  If you are referencing a child portal, please include the child portal in the URL (eg. http://www.mainportal.com/childportal/metapost.ashx).") + "Alias used: " + portalAlias + " calias:" + calias);
				}
				else
				{
					throw new XmlRpcFaultException(0, GetStringGlobal("PortalLoadError", "We tried everything, but we weren't able to identify a valid DotNetNuke portal based on the URL provided.  If you are referencing a child portal, please include the child portal in the URL (eg. http://www.mainportal.com/childportal/metapost.ashx)."));
				}
				//throw new XmlRpcFaultException(0, "The following error occurred:" + exc.Message + exc.StackTrace);
				
			}
		}

    	private void CheckForPortalRights(int portalId)
    	{
			bool activeLicense = false;
			try
			{
				// Retrieve the upload URL from the module's settings
				string sSQL =
					"SELECT ActiveLicense FROM {databaseOwner}{objectQualifier}itcMetaPostPortals (nolock) WHERE PortalId = "
					+ portalId.ToString() + " AND (LicenseEndDate IS NULL OR DateDiff(d, GetDate(), LicenseEndDate) > 0)";
                using (IDataReader dr =
                    (IDataReader)DataProvider.Instance().ExecuteSQL(sSQL))
                {
                    while (dr.Read())
                    {
                        activeLicense = dr.GetBoolean(dr.GetOrdinal("ActiveLicense"));
                    }
                }
			} catch (Exception)
			{
				throw new XmlRpcFaultException(0, GetStringGlobal("PortalAccessError", "This portal is not currently available for publishing.  Please contact your system administrator for more details."));
			}

			if (activeLicense == false) throw new XmlRpcFaultException(0,GetStringGlobal("PortalAccessError", "This portal is not currently available for publishing.  Please contact your system administrator for more details."));
    	}

    	private int GetPortalIDFromAlias(string portalAlias)
		{
			//Get the PortalAlias based on the Request object
			PortalController pc = new PortalController();
			int portalID = -1;
            try
            {
                using (
                    IDataReader dr =
                        ((DataProvider)DotNetNuke.Data.DataProvider.Instance()).GetPortalByAlias(portalAlias))
                {
                    if (dr.Read())
                    {
                        portalID = Convert.ToInt32(dr["PortalID"]);
                    }
                }
            }
            catch (System.ArgumentException)
            {
                try
                {
                    using (IDataReader dr = DotNetNuke.Data.DataProvider.Instance().ExecuteSQL("EXEC {databaseOwner}{objectQualifier}GetPortalByAlias @HTTPAlias = '" + portalAlias + "'"))
                    {
                        if (dr.Read())
                        {
                            portalID = Convert.ToInt32(dr["PortalID"]);
                        }
                    }
                }
                catch (Exception ex)
                {
                    //LogException(ex);
                }
            }
            // Just ignore the errors if any and pass up -1 for the portalID.  
            catch (Exception ex)
            {
                //LogException(ex);
            }
			return portalID;
		}

		private string CreateFoldersForFilePath(string folderPath)
    	{
    		string path = folderPath.Substring(0, folderPath.LastIndexOf(@"\"));
            if (! Directory.Exists(path))
            {
            	Directory.CreateDirectory(path);
            }
    		return folderPath;
    	}

		private void MakeImagesRelative(ref Post post)
		{
			// Make the images relative
			string urlDomain = Context.Request.Url.Host;
			string urlPort = Context.Request.Url.Port.ToString();
			urlPort = (urlPort != "80") ? ":" + urlPort : string.Empty;
			RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.Singleline;
			string expression = @"((?:src|href)\s*?=\s*?"")http[s]*://" + urlDomain + urlPort;
			if (!string.IsNullOrEmpty(post.mt_excerpt))
				post.mt_excerpt = Regex.Replace(post.mt_excerpt, expression, "$1", options);
			if (!string.IsNullOrEmpty(post.description))
				post.description = Regex.Replace(post.description, expression, "$1", options);
			if (!string.IsNullOrEmpty(post.mt_text_more))
				post.mt_text_more = Regex.Replace(post.mt_text_more, expression, "$1", options);

		}
		private void RemoveScript(ref Post post)
		{
			// Remove the expander script tag for Professional Edition content
			string expression = "<script[^>]*?ca0c21fbdc85f6a1597417732d450607.*?</script>";
			RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.Singleline;
			if (!string.IsNullOrEmpty(post.mt_excerpt))
				post.mt_excerpt = Regex.Replace(post.mt_excerpt, expression, string.Empty, options);
			if (!string.IsNullOrEmpty(post.description))
				post.description = Regex.Replace(post.description, expression, string.Empty, options);
			if (!string.IsNullOrEmpty(post.mt_text_more))
				post.mt_text_more = Regex.Replace(post.mt_text_more, expression, string.Empty, options);
		}
		private void AddFooter(ref Post post)
		{
			AddFooter(ref post, -1);
		}
		private void AddFooter(ref Post post, int maxLength)
		{

			// In the future, this should be converted into a pipeline so that we observer the 
			// open closed principle.

			// Check first to see where we should add the footer.  If there's extended content,
			// we'll add it there.  If not, then we'll just add it to the description.
			string footerContent = string.Empty;
			int lengthOfContent = 0;
			bool extendedContentUsed = (!string.IsNullOrEmpty(post.mt_text_more));

			string content = (extendedContentUsed) ? post.mt_text_more : post.description;
			if (ContentContainsImages(content))
			{
				lengthOfContent = HttpUtility.HtmlEncode(content).Length + HttpUtility.HtmlEncode(_expanderScript).Length;
			}
			else
			{
				lengthOfContent = HttpUtility.HtmlEncode(content).Length;
			}

			string virDir =
				MetaPostServices.GetAppPath();

			

			footerContent = MetaPostServices.GetFooterContent(_provider.ProviderKey);

			// Check to see if we should be adding the footer
			if (HttpContext.Current.Request.QueryString["excludefooter"] != null)
			{
				return;
			}

				// See if the MaxLength is set to zero or a value less than 0
				// if so, then set to max value of int.
				if (maxLength <= 0) maxLength = int.MaxValue;

				string filler = string.Empty;

				if (lengthOfContent < maxLength &&
				    lengthOfContent + HttpUtility.HtmlEncode(footerContent).Length > maxLength)
				{
					filler = "".PadRight(maxLength - lengthOfContent, ' ');
				}

				if (extendedContentUsed)
				{
					// For some providers, the extended content is used, but should be ingored
					// For now this is just hard coded
					if (_provider.ManifestFilePath.EndsWith("wlwforum.xml"))
					{
						post.description = post.description + filler + footerContent;
					}
					else
					{
						post.mt_text_more = content + filler + footerContent;	
					}
				}
				else
				{
					post.description = content + filler + footerContent;
				}
		}
		private string GetFooterScriptIncludePath(IPublishable _provider)
		{
			string scriptIncludePath = "/DesktopModules/itcMetaPost/js/mg.js";
			PropertyInfo pi = _provider.GetType().GetProperty("FooterScriptIncludePath");
			if (pi != null)
			{
				scriptIncludePath = (string)pi.GetValue(_provider, null);
			}
			return scriptIncludePath;
		}
		private void getProvider()
		{
			IPublishable provider = null;
			
			ProviderStruct providerInfo = CommonServices.GetProviderInfo(_moduleName);

			object[] methodParams;

			if (string.IsNullOrEmpty(providerInfo.ProviderAssemblyName) || string.IsNullOrEmpty(providerInfo.ProviderTypeName))
			{
				// Loop through the dlls in the bin directory looking to see if this 
				// Provider has been installed.  If so, we add it to the list of providers.
				Assembly[] appAssemblies = AppDomain.CurrentDomain.GetAssemblies();
				foreach (Assembly assembly in appAssemblies)
				{
					// Here for testing
                    //object aTest = assembly;
                    //aTest = null;
					try
					{
						// Check to see if the assembly contains a class with our interface
						foreach (Type type in assembly.GetTypes())
						{
							if (type.IsClass)
							{
								try
								{
									foreach (Type iface in type.GetInterfaces())
									{
										if (iface.Equals(typeof (IPublishable)))
										{
											//CheckDbForType(type);

											IPublishable tempProvider = (IPublishable) Activator.CreateInstance(type);
											if (tempProvider.ProviderKey.ToLower() == _moduleName.ToLower()
												&& (type.Name != "metaPostDNNArtilceProvider"						// Added to ensure that
													|| (type.Name == "metaPostDNNArtilceProvider"					// while changes are being made to the 
														&& type.Assembly.FullName.StartsWith("MetaPostProvider."))))// code we use the version of the 
											{																		// DNNArticle provider that ships with metaPost.
												// We have a match on the provider, so persist the provider information to the 
												// database.
												DataProvider dp = DotNetNuke.Data.DataProvider.Instance();
												methodParams = new object[4];
												methodParams.SetValue(tempProvider.ProviderKey, 0);
												methodParams.SetValue(assembly.FullName.Substring(0, assembly.FullName.IndexOf(',')), 1);
												methodParams.SetValue(type.FullName, 2);
												methodParams.SetValue(tempProvider.ManifestFilePath, 3);

												dp.ExecuteNonQuery("itcMetaPost_Insert_Provider", methodParams);

												provider = (IPublishable) (Activator.CreateInstance(type));
												goto exit_loop;
											}
										}
									}
								}
								catch (Exception ex)
								{
									//object error = ex;
								} // Ignore this error and continue
							}
						}
					} 
					catch (Exception ex)
					{
						object error = ex;
					} //Ignore this error and continue.
				}
			}
			else
			{
				provider = (IPublishable)(Activator.CreateInstance(providerInfo.ProviderAssemblyName, providerInfo.ProviderTypeName).Unwrap());
			}

		exit_loop:

			if (provider == null) throw new XmlRpcFaultException(0,"An error occurred loading your provider.  Please check the provider key you entered.  Make sure it is set to the Friendly Name of the module to which you are trying to connect and be sure to include any spaces in the friendly name (e.g. ?key=news articles would load the Ventrian News Articles module).");
			
			// 04/09/08 - DW - Removed in an effort to centralize localization.
			//CommonServices.LocalizationFilePath = provider.LocalizationFilePath;

			_provider = provider;
		}

		private Item getItemFromPost(Post content)
		{
			Item item = new Item();

			item.AllowComments			= content.mt_allow_comments ?? -1;
			item.AllowTrackbacksOrPings = content.mt_allow_pings;
			item.Categories				= content.categories;
			item.Content				= content.description;
			item.DateCreated			= content.dateCreated;
			item.StartDate				= content.pubDate;
			item.ExtendedContent		= content.mt_text_more;
			item.ItemId					= content.postid;
			item.Keywords				= content.mt_keywords;
			item.Link					= content.link;
			item.Permalink				= content.permalink;
			item.PingUrls				= content.mt_tb_ping_urls;
			item.Title					= content.title;
			item.SeoFriendlyTitle		= content.wp_slug;
			item.ItemPassword			= content.wp_password;
			// No longer user - left for reference.
			//// Note: this is a workaround for strange behavior in WLW where 
			//// parentIds are shown in the parent page dropdown rather than
			//// the name of the parent.  To work around this issue, we go 
			//// ahead and save the name of the parent page and then make 
			//// sure in the edit item procedure to ignore this value.
			//int parentId = 0;
			//if (int.TryParse(content.wp_page_parent_id, out parentId))
			//item.ParentId				= parentId.ToString();
			item.ParentId				= content.wp_page_parent_id;
			item.PageOrder				= content.wp_page_order;
			item.AuthorId				= content.wp_author_id;

			item.Summary				= content.mt_excerpt;
			item.PingUrls				= content.mt_tb_ping_urls;

			return item;
		}

		private Post getPostFromItem(Item item)
		{
			Post post = new Post();

			post.mt_allow_comments		= item.AllowComments;
			post.mt_allow_pings			= item.AllowTrackbacksOrPings;
			post.categories				= item.Categories;
			post.description			= item.Content;
			post.dateCreated			= item.DateCreated;
			post.pubDate				= item.StartDate;
			post.date_created_gmt		= item.DateCreated;
			post.mt_text_more			= item.ExtendedContent;
			post.postid					= item.ItemId;
			post.mt_keywords			= item.Keywords;
			post.link					= item.Link;
			post.permalink				= item.Permalink;
			post.mt_tb_ping_urls		= item.PingUrls;
			post.title					= item.Title;
			post.wp_slug				= item.SeoFriendlyTitle;
			post.wp_password			= item.ItemPassword;
			post.wp_page_parent_id		= item.ParentId;
			post.wp_page_order			= item.PageOrder;
			post.wp_author_id			= item.AuthorId;
			post.mt_excerpt				= item.Summary;
			post.mt_tb_ping_urls		= item.PingUrls;

			return post;
		}

		private Page getPageFromItem(Item item)
		{
			Page page = new Page();

			page.page_id = item.ItemId;
			page.page_title = item.Title;
			if (string.IsNullOrEmpty(item.ParentId) || item.ParentId == "0")
				page.page_parent_id = "0";
			else
				page.page_parent_id = item.ParentId.ToString();
			page.dateCreated = item.DateCreated;

			return page;
		}

		/*
		public int page_id;
		public string page_title;
		public int page_parent_id;
		public DateTime DateCreated;*/

		private Post[] getPostsFromItems(Item[] items)
		{
			Post[] posts = new Post[items.Length];
			for (int i = 0; i < items.Length; i++)
			{
				posts[i] = getPostFromItem(items[i]);
			}
			return posts;
		}

		private Page[] getPagesFromItems(Item[] items)
		{
			Page[] pages = new Page[items.Length];
			for (int i = 0; i < items.Length; i++)
			{
				pages[i] = getPageFromItem(items[i]);
			}
			return pages;
		}

		private ItemCategory[] GetItemCategoriesFromPostedCategories(PostedCategory[] postedCategories)
		{
			List<ItemCategory> itemCategoryList = new List<ItemCategory>();

			foreach (PostedCategory pc in postedCategories)
			{
				ItemCategory ic = new ItemCategory();
				ic.CategoryId = pc.categoryId;
				ic.IsPrimary = pc.isPrimary;
				itemCategoryList.Add(ic);
			}

			return itemCategoryList.ToArray();
		}

		private PostedCategory[] GetPostedCategoriesFromItemCategories(ItemCategory[] itemCategories)
		{
			List<PostedCategory> postedCategoryList = new List<PostedCategory>();

			foreach (ItemCategory ic in itemCategories)
			{
				PostedCategory pc = new PostedCategory();
				pc.categoryId = ic.CategoryId;
				pc.categoryName = ic.CategoryName;
				pc.isPrimary = ic.IsPrimary;
				postedCategoryList.Add(pc);
			}

			return postedCategoryList.ToArray();
		}

		private CategoryInfo getCategoryInfoFromItemCategoryInfo(ItemCategoryInfo ici)
		{
			CategoryInfo ci = new CategoryInfo();
			ci.categoryId = ici.CategoryId.ToString();
			ci.categoryName = ici.CategoryName;
			ci.description = ici.Description;
			ci.htmlUrl = ici.HtmlUrl;
			ci.parentId = ici.ParentId.ToString();
			ci.rssUrl = ici.RssUrl;

			return ci;
		}

		private CategoryInfo[] getCategoryInfosFromItemCategoryInfos(ItemCategoryInfo[] ici)
		{
			CategoryInfo[] ci = null;
			if (ici != null)
			{
				ci = new CategoryInfo[ici.Length];
				for (int i = 0; i < ici.Length; i++)
				{
					ci[i] = getCategoryInfoFromItemCategoryInfo(ici[i]);
				}
			}
			return ci;
		}

		private MetaWebLogCategoryInfo getMetaWebLogCategoryInfosFromItemCategoryInfo(CategoryInfo ci)
		{
			MetaWebLogCategoryInfo mwci = new MetaWebLogCategoryInfo();
			mwci.description = ci.description;
			mwci.htmlUrl = ci.htmlUrl;
			mwci.rssUrl = ci.rssUrl;
			return mwci;
		}

		private MetaWebLogCategoryInfo[] getMetaWebLogCategoryInfosFromCateogryInfos(CategoryInfo[] ci)
		{
			MetaWebLogCategoryInfo[] mwci = new MetaWebLogCategoryInfo[ci.Length];
			for (int i = 0; i < ci.Length; i++)
			{
				mwci[i] = getMetaWebLogCategoryInfosFromItemCategoryInfo(ci[i]);
			}
			return mwci;
		}

		private void InitializeMethodCall(string username, string password)
		{
			try
			{
				GetPortalSettings();
				getProvider();
				_userInfo = ValidateUser(username, password, this.Context.Request.UserHostAddress);
				HttpContext.Current.Items["UserInfo"] = _userInfo;
			}
			catch (Exception ex)
			{
				LogException(ex);
				throw;
			}
		}

		private string GetSummary(ref Post content)
		{
			string summary = string.Empty;
			
			if (!string.IsNullOrEmpty(content.mt_excerpt))
			{
				summary = content.mt_excerpt;
			}
			else
			{
				summary = content.description;
			}

			return summary;
		}

		private string GetPostText(ref Post content)
		{
			string postContent = string.Empty;
			if (string.IsNullOrEmpty(content.mt_excerpt) 
				&& !string.IsNullOrEmpty(content.mt_text_more)
				&& !string.IsNullOrEmpty(content.description))
			{
				postContent = content.mt_text_more;
			}
			else
			{
				postContent = content.description;
			}
			return postContent;
		}

		private void HandleTrackbacksOrPings(string moduleLevelId, string itemId, ref Post content, bool publish, ILinkable provider, bool autoDiscovery)
		{

			// Send Pings or Trackbacks if publishing this page and there are trackbacks
			if (publish)
			{
				// Retrieve the Blog Name
				string blogName = provider.GetModuleName(moduleLevelId, _userInfo, _portalSettings);
				string permaLink = provider.GetPermaLink(moduleLevelId, itemId, _userInfo, _portalSettings);

				TrackingService.TrackbackOrPing(content.mt_tb_ping_urls, content.title, permaLink, blogName,
											GetSummary(ref content), GetPostText(ref content), autoDiscovery, _portalSettings);
			}
		}

		private bool IsStyleDetectionPost(Post post)
		{
			bool styleDetectionPost;
			styleDetectionPost = (post.title.Length > 100
								  && post.title.Contains("3bfe001a-32de-4114-a6b4-4005b770f6d7"));
			return styleDetectionPost;
		}

		/// <summary>
		/// Not currently used.  This procedure was added to include the URLs that are being pinged
		///		However, it wasn't used becuase there isn't a good way to determine which pings 
		///		Require a URL in the page and which don't.  For now, we'll let the author add ping 
		///		URLs appropriately.  If needed, we'll use this procedure at a later date.
		/// DW - 2/7/2008
		/// </summary>
		/// <param name="content"></param>
		private void AddTrackbacksOrPings(ref Post content)
		{
			if (content.mt_tb_ping_urls.Length > 0)
			{
				string trackDiv = "<div style=\"display:none;\">";
				foreach (string url in content.mt_tb_ping_urls)
				{
					trackDiv += string.Format("<a href=\"{0}\">{0}</a><br />", url);
				}
				trackDiv += "</div>";
				content.description += trackDiv;
			}
			return;
		}

		private string GetString(string localizationKey, string defaultValue)
		{
			return MetaPostServices.GetString(localizationKey, defaultValue, _provider.LocalizationFilePath, _portalSettings.DefaultLanguage);
		}
		private string GetStringGlobal(string localizationKey, string defaultValue)
		{
			// DW - 03/08/2009 - Note that at this point we may not know anything about the portal
			//						if this is the case, then we'll have to assume en-US for the default
			//						language.
			string defaultLanguage = "en-US";
			if (_portalSettings != null) defaultLanguage = _portalSettings.DefaultLanguage; 
			return MetaPostServices.GetStringGlobal(localizationKey, defaultValue, defaultLanguage);
		}

		private void LogException (Exception ex)
		{
			try
			{
				DotNetNuke.Services.Exceptions.Exceptions.LogException(ex);
			}
			catch {}
		}

		private Hashtable getmetaPostSettings()
		{
			Hashtable returnTable = new Hashtable();
			string sURL = null;
			string allowedUploadExtensions = null;

			try
			{
				// Retrieve the upload URL from the module's settings
				string sSQL =
					"SELECT * FROM {databaseOwner}{objectQualifier}itcMetaPost (nolock) WHERE ItemId = 1";
                using (IDataReader dr =
                    (IDataReader)DataProvider.Instance().ExecuteSQL(sSQL))
                {
                    while (dr.Read())
                    {
                        sURL = dr["URL"].ToString() + "";
                        allowedUploadExtensions = dr["AllowedUploadExtensions"] as string;
                    }
                }
			}
			catch
			{
			} // If we fail, we just set to a default value.

			// Make sure we don't have a beginning forward slash.
			if (!string.IsNullOrEmpty(sURL) && sURL.Substring(0, 1) == "/") sURL = sURL.Substring(1);

			// Set to default value in case this value can't be retrieved for some reason.
			if (string.IsNullOrEmpty(sURL)) sURL = "images/[Provider]/";

			// Make sure we have an entry in the AllowedUploadExtensions field
			if (string.IsNullOrEmpty(allowedUploadExtensions)) allowedUploadExtensions = "jpg,zip,rar,gif,png";

			returnTable.Add("URL", sURL);
			returnTable.Add("AllowedUploadExtensions", allowedUploadExtensions);


			return returnTable;
		}

		private void AddImageExpander(ref Item item)
		{
			string groupTitle = string.Empty;
			if (!string.IsNullOrEmpty(item.Summary)) item.Summary = ExpandImages(item.Summary, ref groupTitle);
			if (!string.IsNullOrEmpty(item.Content)) item.Content = ExpandImages(item.Content, ref groupTitle);
			if (!string.IsNullOrEmpty(item.ExtendedContent)) item.ExtendedContent = ExpandImages(item.ExtendedContent, ref groupTitle);
		}

		private string ExpandImages(string input,ref string groupTitle)
		{
			if (ContentContainsImages(input))
			{
				// Regex for adding expando effect class and JavaScript to the links for images.
				string regex1 =
					@"(?<firstpart><a[^>]+?(?:png|jpg|jpeg|gif)[^>]+?)(?<anchorend>>)(?<secondpart>[^<]*?<img[^>]+?(?:png|jpg|jpeg|gif)[^>]+>[^<]*?</a>)";
				// Regex for adding the title attribute to the image
				string regex2 =
					@"(?<firstpart><a[^>]+?(?:png|jpg|jpeg|gif)[^>]+?>[^<]*?<img[^>]+?alt="")(?<alt>[^""]+?)(?<thirdpart>"")(?<fourthpart>[^>]+>[^<]*?</a>)";
				// Regex for adding iframe expando to URLs
				string regex3 =
					@"(?<firstpart><a[^>]+?)(?<secondpart>rel=""\s*mp\s*)(?<thirdpart>,*)(?<rel>[^""]*)(?<fifthpart>[^>]+>.*?</a>)";
				// Regex for adding HTML Content expando (Link)
				string regex4 =
					@"(?<firstpart><a[^>]+?)(?<secondpart>href="")(?<thirdpart>mp)(?<fourthpart>\d+)(?<fifthpart>,*)(?<settings>[^""]*)(?<seventhpart>[^>]*?>.*?</a>)";
				// Regex for adding HTML Content expando (Detail)				
				string regex5 =
					@"(?<firstpart><(?:p|div)>\s*){0,1}\[mp(?<secondpart>\d+)[^\]]*?](?<thirdpart>.*?)\[/mp\2](?<lastpart></(?:p|div)>){0,1}";
				// Regex for auto tabview
				string regex6 =
                    @"(?:<p[^>]*>|<div[^>]*>)(?:\s|&#160;|&nbsp;)*(?:<font[^>]*>)*(?:\n|\s|&nbsp;)*(?<firstpart>(?:<a[^>]+?href=""tb\d+,*[^""]*[^>]*?>.*?</a>[^\[]*)+)(?<secondpart>(?:(?:[^\[])*\[tb(?<thirdpart>\d+?)[^\]]*?].*?\[/tb\3])+)(?:\n|\s|&nbsp;)*(?:</font>)*(?:\s|&#160;|&nbsp;)*(?:</p>|</div>)";
				// Regex for auto video expander
				string regex7 =
					@"<a[^>]+?href=""(?<anchor>[^""]+?(?:swf|flv))\s*""[^>]+?rel=""(?<id>mp\d*),*(?<attributes>[^>]+?)""[^>]*>(?<content>.*?)</a>";
				string regex8 =
					@"<a[^>]+?href=""(?<anchor>[^""]+?)\s*""[^>]+?rel=""(?<id>mp\d*),*(?<attributes>[^>]+?),(?<type>type=video)""[^>]*>(?<content>.*?)</a>";

				RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.Singleline;

				// Try to find an imageGroup name to use.  First look to see if one exists in the content.
				// If not, then we'll generate one.
				if (groupTitle == string.Empty)
				{
					// Use regex to look for an existing groupTitle in the content
					string regexGroupTitle = @"slideshowGroup:'(?<firstpart>\w{8})'}";
					Match groupTitleMatch = Regex.Match(input, regexGroupTitle, options);
					if (groupTitleMatch.Success)
						groupTitle = groupTitleMatch.Groups["firstpart"].Value;
					// If one doesn't exist, then we'll generate one!
					if (groupTitle == string.Empty)
					{
						groupTitle = Guid.NewGuid().ToString().Substring(0, 8);
					}
				}
					
				string retValue =
					Regex.Replace(input, regex1, "$1  class=\"itcexpando\" onclick=\"return mp.expand(this,{slideshowGroup:'" + groupTitle + "'})\" $2$3", options);
				retValue =
					Regex.Replace(retValue, regex2, "$1$2$3 title=\"$2\"$4", options);

				// Remove the default image alt added by WLW that was added to the title attribute
				retValue = retValue.Replace("title=\"image\"", string.Empty);

				// Auto Video Expander
				retValue = Regex.Replace(retValue, regex7,
								new MatchEvaluator(VideoEvaluator), options);
				retValue = Regex.Replace(retValue, regex8,
								new MatchEvaluator(VideoEvaluator), options);

				if (!string.IsNullOrEmpty(videoViewers))
				{
					videoViewers = "<!--Begin itc_Video_Viewers-->" + videoViewers +
					               "<!--End itc_Video_Viewers-->";
					retValue += videoViewers;
				}

				// Auto Link Expander
				retValue =
					Regex.Replace(retValue, regex3,
								  "$1 class=\"itcexpando\" onclick=\"return mp.htmlExpand(this, { objectType: 'iframe'$3 $4} )$5", options);
				
				// Set a default value if no width or height are passed in.
				retValue =
					retValue.Replace("{ objectType: 'iframe' }", "{ objectType: 'iframe',width:1024,height:768 }");

				// Html Content expando Link
				retValue = Regex.Replace(retValue, regex4,
								@"<!--Begin mp_html_link_$4_itc_uid_-->$1 $2#"" class=""itcexpando"" onclick=""return mp.htmlExpand(this, { contentId:'mp_html_$4_itc_uid_' $5 $6} )$7<sup class=""itcexpand-super"">$4</sup><!--End mp_html_link_$4_itc_uid_-->", options);

				// Html Content expando Detail
				retValue = Regex.Replace(retValue, regex5,
								new MatchEvaluator(ContentHtmlEvaluator), options);

				// Auto Tabview
				retValue = Regex.Replace(retValue, regex6,
				                new MatchEvaluator(TabViewEvaluator), options);


				// Check to see if a unique id was added to content expander regions
				// If so, replace with a unique 8 digit id
				if (retValue.Contains("_itc_uid_"))
				{
					//string uid = System.Guid.NewGuid().ToString().Substring(0, 8);
					retValue = retValue.Replace("_itc_uid_", "_" + groupTitle);
				}
				
				// Not needed.  We'll just have people enter in mp,width:100,height:200 format
				//MatchCollection mcAttributes = Regex.Matches(retValue, regex4, options);
				//foreach (Match m in mcAttributes)
				//{
				//    string attributes = m.Groups["attributes"].Value;
				//    if (!string.IsNullOrEmpty(attributes))
				//    {
				//        attributes = attributes.Replace("=", ":");
				//    }
				//    retValue = retValue.Replace(m.Value, "objectType: 'iframe', " + attributes + "}");
				//}

				// Replace the mp,width=nnn syntax with width:200, syntax

				if (retValue.Contains("ca0c21fbdc85f6a1597417732d450607"))
				{
					return retValue;
				}
				else
				{
					return _expanderScript + retValue;
				}
			}
			else
			{
				return input;	
			}
		}

    	private string videoViewers;
    	private string VideoEvaluator(Match match)
    	{
    		string retValue;
    		string url = match.Groups["anchor"].Value;
    		string attributes = match.Groups["attributes"].Value;
			string content = match.Groups["content"].Value;
			string id = match.Groups["id"].Value;
    		string linkType = string.Empty;
			if (match.Groups["type"] != null) linkType = match.Groups["type"].Value;
			string appPath = MetaPostServices.GetAppPath();
    		string upgradeVideo =
    			GetStringGlobal("FlashUpgradeMessage", "An upgrade of your Flash video player may be needed.");
    		int width = 1024;
    		int height = 768;
    		RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.Singleline;

    		try {width = Convert.ToInt32(Regex.Match(attributes, @"width:(?<width>\d*)", options).Groups["width"].Value);} catch {}
			try {height = Convert.ToInt32(Regex.Match(attributes, @"height:(?<height>\d*)", options).Groups["height"].Value);} catch {}

			retValue = "<script type=\"text/javascript\" src=\"" + appPath + "/Desktopmodules/itcMetaPost/js/itcexpand/swfobject.js\"></script>"
						+ "<!--Begin mp_video_" + id + "-->\n"
						+ "<!--" + linkType + "-->\n"
						+ @"<!--url:" + url + @"--><!--width:" + width.ToString() + @"--><!--height:" + height.ToString() + "--><script type=\"text/javascript\">\n"
						+ "                                // <![CDATA[                     \n"
						+ "                                                var itc_vid_" + id + @" = new SWFObject( """ + url + @""", ""csSWF"", """ + width.ToString() + @""", """ + height.ToString() + "\", \"9.0.28\", \"#eeeeee\");\n"
						+ "                                                // itc_vid_" + id + ".addParam( \"quality\", \"best\" );\n"
						+ "                                                itc_vid_" + id + ".addParam(\"wmode\", \"transparent\");\n"
						+ "                                                itc_vid_" + id + ".addParam( \"allowFullScreen\", \"true\" );\n"
						+ "                                                itc_vid_" + id + ".addParam( \"scale\", \"showall\" );\n"
						+ "                                                itc_vid_" + id + ".addParam( \"allowScriptAccess\", \"always\" );\n"
						+ "                                                itc_vid_" + id + ".addVariable( \"autostart\", \"true\" );\n"
						+ "\n"
						+ "                                // ]]>\n"
						+ " </script>\n"
						+ @"<a class=""itcexpando"" onclick=""return mp.htmlExpand(this, { swfObject: itc_vid_" + id + @", contentId: 'itcexpand-html-" + id + "', \n"
						+ @"                                                                allowSizeReduction: false, wrapperClassName: 'itcexpand-white', outlineType: 'rounded-white'," + "\n"
						+ @"                                                                outlineWhileAnimating: true, preserveContent: false} )"" href=""#"" getparams=""null"" getparams=""null""><!--Begin mp_video_content-->" + content + "<!--End mp_video_content--></a>\n"
						+ @"<!--End mp_video_" + id + "-->\n";
			
			// Keep track of the video viewer regions in a private string.  We'll add these at the bottom of the 
			// viewer.  This is necessary to ensure that inline elements such as the <p> tag don't contain block elements
			videoViewers += @" <div class=""itcexpand-html-content"" id=""itcexpand-html-" + id + @""" style=""width: " + width.ToString() + @"px; border-top-style: none; border-right-style: none; border-left-style: none; height: " + (height + 25).ToString() + @"px; text-align: right; border-bottom-style: none""> <div class=""itcexpand-move"" style=""height: 20px""><a class=""control"" style=""color: #666"" onclick=""return mp.close(this)"" href=""#"">Close</a> </div> <div class=""itcexpand-body""><strong>" + upgradeVideo + "</strong></div></div>\n";

    		return retValue;
    	}

    	private string ContentHtmlEvaluator(Match match)
		{
			string retValue;
    		string prefixTag = match.Groups["firstpart"].Value;
    		string postfixTag = match.Groups["lastpart"].Value;
			string contentId = match.Groups["secondpart"].Value;
			string content = match.Groups["thirdpart"].Value;
			string regexHtmlContent = @"\[mp(?<firstpart>\d+)[^\]]*?](?<secondpart>.*?)\[/mp\1]";
			RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.Singleline;

			// Search the content recursively for similar matches
			content = Regex.Replace(content, regexHtmlContent,
				new MatchEvaluator(ContentHtmlEvaluator), options);

			// build replacement string for Html Content Expando Detail
			retValue = "<!--Begin mp_html_detail_" + contentId + "_itc_uid_-->\n"
				+ "<div class=\"itcexpand-html-content\" id=\"mp_html_" + contentId + "_itc_uid_\">\n"
				+ "	<div class=\"itcexpand-header\">\n"
				+ "		<ul>\n"
				+ "			<li class=\"itcexpand-move\">\n"
				+ "				<a href=\"#\" onclick=\"return false\">" + GetStringGlobal("Move", "Move") + "</a>\n"
				+ "			</li>\n"
				+ "			<li class=\"itcexpand-close\">\n"
				+ "				<a href=\"#\" onclick=\"return mp.close(this)\">" + GetStringGlobal("Close", "Close") + "</a>\n"
				+ "			</li>\n"
				+ "		</ul>	    \n"
				+ "	</div>\n"
				+ "	<div class=\"itcexpand-body\"><sup class=\"itcexpand-super\">" + contentId + "</sup><!--Begin mp_html_detail_body_" + contentId + "_itc_uid_-->\n"
				+ prefixTag + content + postfixTag + "<!--End mp_html_detail_body_" + contentId + "_itc_uid_--></div>\n"
				+ "    <div class=\"itcexpand-footer\">\n"
				+ "        <div>\n"
				+ "            <span class=\"itcexpand-resize\" title=\"Resize\">\n"
				+ "                <span></span>\n"
				+ "            </span>\n"
				+ "        </div>\n"
				+ "    </div>\n"
				+ "</div>\n"
				+ "<!--End mp_html_detail_" + contentId + "_itc_uid_-->\n";

			return retValue;
		}
		private string TabViewEvaluator(Match match)
		{
			string retValue = string.Empty;
			string regexLinks = @"<a[^>]+?href=""tb(?<tabId>\d+),*[^""]*[^>]*?>(?<content>.*?)</a>.*?(?=$|<a)";
            String regexTabs = @"(?:(?:[^\[])*\[tb(?<firstpart>\d+)[^\]]*?](?:</font>)*(?:\s|&#160;|&nbsp;)*(?:</(?:div|p)>)*(?<content>.*?)(?:<(?:div|p)>)*(?:\s|&#160;|&nbsp;)*(?:<font[^>]*>)*\[/tb\1])";
			string linkContent = match.Groups["firstpart"].Value;
			string tabContent = match.Groups["secondpart"].Value;
			string uniqueId = System.Guid.NewGuid().ToString().Substring(0, 8);
			RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.Singleline;

			// First, we start the container div off
			retValue = "<!--Begin mp_tabs_" + uniqueId + "-->\n";
			retValue += "<script>var itc_f=function(){if(typeof YAHOO != 'undefined'){var tabView = new YAHOO.widget.TabView('" + uniqueId + "');clearInterval(itc_i);}};var itc_i=setInterval('itc_f();',100);</script>\n";
			// Begin the Outer Div
			retValue += "<div class=\"yui-skin-sam\"><div id=\"" + uniqueId + "\" class=\"yui-navset\">\n";
				// Begin the UL
				retValue += "    <ul class=\"yui-nav\">\n";
				retValue += "	 <!--Begin mp_tabs_links_" + uniqueId + "-->\n";
				// Replace the Tab Links
				linkContent = Regex.Replace(linkContent, regexLinks, "<li><a href=\"#itc_tab_$1_" + uniqueId + "\"><em>$2</em></a></li>", options);
				retValue += linkContent;

				// Close the ul
				retValue += "	 <!--End mp_tabs_links_" + uniqueId + "-->\n";
				retValue += "    </ul>\n";
				// Begin the content div
				retValue += "    <div class=\"yui-content\">\n";
				retValue += "	 <!--Begin mp_tabs_content_" + uniqueId + "-->\n";

				// Replace the Tab Pages
                tabContent = Regex.Replace(tabContent, regexTabs, "<div id=\"itc_tab_$1_" + uniqueId + "\">$2<hr class=\"itc_tab_sep\" /></div><!--End div id=\"itc_tab_$1_" + uniqueId + "\"-->", options);
                // Check the content to make sure the beginning and ending tags are symmetrical
                string symRegEx = @"(?<firstpart><div id=""{0,1}itc_tab_(?<secondpart>\d*)_(?<thirdpart>\w{8})""{0,1}>)(?<content>.*?)(?<lastpart><hr[^c]+class=""{0,1}itc_tab_sep""{0,1}[^>]*>[^<]*</div>[^<]*<!--End div id=""{0,1}itc_tab_\2_\3""{0,1}-->)";
                tabContent = Regex.Replace(tabContent, symRegEx, new MatchEvaluator(TabContentEvaluator), options);
				retValue += tabContent;
				
				// Close the Content Div
				retValue += "	 <!--End mp_tabs_content_" + uniqueId + "-->\n";
				retValue += "    </div>\n";
			// Close the Outer Div
			retValue += "</div></div>\n";
			retValue += "<!--End mp_tabs_" + uniqueId + "-->\n";

			retValue =
				retValue.Replace("<!--Begin mp_tabs_links_" + uniqueId + "-->\n<li>",
								 "<!--Begin mp_tabs_links_" + uniqueId + "-->\n<li class=\"selected\">");
			
			return retValue;
		}
        /// <summary>
        /// Called to make sure the beginning and ending content tags are symmetrical.  If they aren't, we add the 
        /// missing tag.  Right now, this procedure just assumes that we have either a p or div tag.
        /// </summary>
        /// <param name="match"></param>
        /// <returns></returns>
        private string TabContentEvaluator(Match match)
        {

            string firstPart = match.Groups["firstpart"].Value;
            string content = match.Groups["content"].Value;
            string lastPart = match.Groups["lastpart"].Value;

            // Check to make sure the beginning and ending tags in the content are symmetrical.  
            if (content.StartsWith("<p", true, System.Globalization.CultureInfo.InvariantCulture) && !content.EndsWith("</p>", true, System.Globalization.CultureInfo.InvariantCulture))
            {
                content = content + "</p>";
            }
            else if (content.EndsWith("</p>", true, System.Globalization.CultureInfo.InvariantCulture) && !content.StartsWith("<p", true, System.Globalization.CultureInfo.InvariantCulture))
            {
                content = "<p>" + content;
            }
            else if (content.StartsWith("<div", true, System.Globalization.CultureInfo.InvariantCulture) && !content.EndsWith("</div>", true, System.Globalization.CultureInfo.InvariantCulture))
            {
                content = content + "</div>";
            }
            else if (content.EndsWith("</div>", true, System.Globalization.CultureInfo.InvariantCulture) && !content.StartsWith("<div", true, System.Globalization.CultureInfo.InvariantCulture))
            {
                content = "<div>" + content;
            }

            return firstPart + content + lastPart;
        }
		private string ContentHtmlReverseEvaluator(Match match)
		{
			string retValue;
			string contentId = match.Groups["firstpart"].Value;
			string content = match.Groups["secondpart"].Value;
			string regexHtmlContent = @"<!--Begin mp_html_detail_(?<firstpart>\d*)(?:_\w{8})*-->.*<!--Begin mp_html_detail_body_\1(?:_\w{8})*-->(?<secondpart>.*)<!--End mp_html_detail_body_\1(?:_\w{8})*-->.*<!--End mp_html_detail_\1(?:_\w{8})*-->";
			RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.Singleline;

			// Search the content recursively for similar matches
			content = Regex.Replace(content, regexHtmlContent,
				new MatchEvaluator(ContentHtmlReverseEvaluator), options);

			// build replacement string for Html Content Expando Detail
			retValue = "<p>[mp" + contentId + "]" + content + "[/mp" + contentId + "]</p>";

			return retValue;
		}
		private string ContentTabsReverseEvaluator(Match match)
		{
			// When we get here, we have the entire contents of a single tabview 
			// in the page.  We'll need to extract the links and the content
			// back into the notation which can be shown in WLW.
			string retValue = string.Empty;

			try
			{
				string links = string.Empty;
				string content = string.Empty;
				string tabsFullContent = match.Groups["secondpart"].Value;
				string regexLinks =
					@"<!--Begin mp_tabs_links_(?<firstpart>\w{8})-->(?<secondpart>.*)<!--End mp_tabs_links_\1-->";
				string regexContent =
					@"<!--Begin mp_tabs_content_(?<firstpart>\w{8})-->(?<secondpart>.*)<!--End mp_tabs_content_\1-->";
				RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.Singleline;

				links = Regex.Match(tabsFullContent, regexLinks, options).Groups["secondpart"].Value;
				content = Regex.Match(tabsFullContent, regexContent, options).Groups["secondpart"].Value;

				retValue = "<p>";
				retValue +=
					Regex.Replace(links,
								  @"<li[^>]*>[^<]*<a[^>]+href=""{0,1}#itc_tab_(?<firstpart>\d*)_(?<secondpart>\w{8})""{0,1}>[^<]*<em>(?<thirdpart>.*?)</em>[^<]*</a>",
					              "<a href=\"tb$1\">$3</a>\n", options);
				retValue += "</p>";
				retValue +=
					Regex.Replace(content,
								  @"<div id=""{0,1}itc_tab_(?<firstpart>\d*)_(?<secondpart>\w{8})""{0,1}>(?<thirdpart>.*?)<hr[^c]+class=""{0,1}itc_tab_sep""{0,1}[^>]*>[^<]*</div>[^<]*<!--End div id=""{0,1}itc_tab_\1_\2""{0,1}-->",
					              "<p>[tb$1]</p>$3<p>[/tb$1]</p>", options);
			} catch
			{
				// Just show the original HTML 
				retValue = match.Value;
			}
			return retValue;
		}

		private void RecordRedirectURL(string entryID, int portalID, string ipAddress, IPublishable provider, string redirectURL)
		{

		}

		private string VideoReverseEvaluator(Match match)
		{
			// When we get here, we have the entire contents of a video link.
			// Our job here is to parse this information and return the content
			// back to a simple link in the following form:
			// <a href="url_to_a_swf.swf" rel="mp,width:1024,height:768">Link Content</a>
			
			string retValue;

			try
			{
				string id = match.Groups["id"].Value;
				string url = match.Groups["url"].Value;
				string width = match.Groups["width"].Value;
				string height = match.Groups["height"].Value;
				string content = match.Groups["content"].Value;
				string type = string.Empty;
                if (match.Groups["type"] != null && match.Groups["type"].ToString() != string.Empty) type = "," + match.Groups["type"].Value;

				retValue = @"<a href=""" + url + @""" rel=""mp" + id + ",width:" + width + @",height:" + height + type + @""">" + content + @"</a>";
			}
			catch
			{
				// Just show the original HTML 
				retValue = match.Value;
			}
			return retValue;
		}

		private bool ContentContainsImages(string content)
		{
			bool containsImages = false;
			bool containsUrlsToBeExpanded = false;
			bool containsHtmlContent = false;
			bool containsTabs = false;
			bool containsVideo = false;
			List<String> queryStringItems = new List<string>();
			string scriptQueryString = string.Empty;
			RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.Singleline;

			// Check for images
			string regexCI =
				@"<a[^>]+?(?:png|jpg|jpeg|gif)[^>]+?>[^<]*?<img[^>]+?(?:png|jpg|jpeg|gif)[^>]+>[^<]*?</a>";
			containsImages = (Regex.Match(content, regexCI, options).Length > 0);

			// Check for IFrame entries.
			regexCI =
				@"<a[^>]+?rel=""mp[^>]+?>.*?</a>";
			containsUrlsToBeExpanded = (Regex.Match(content, regexCI, options).Length > 0);

			// Check for HTML Content
			regexCI =
				@"<a[^>]+?href=""mp[^>]+?>.*?</a>";
			containsHtmlContent = (Regex.Match(content, regexCI, options).Length > 0);

			// Check for Tab Content
			regexCI =
				@"<a[^>]+?href=""tb\d+[^>]+?>.*?</a>";
			containsTabs = (Regex.Match(content, regexCI, options).Length > 0);

			// Removed since the SWFObject script is added above each video.
			// Check for Video Content
			regexCI =
				@"<a[^>]+?(?:swf|flv)[^>]+?rel=""mp[^>]+?>.*?</a>";
			containsVideo = false; //(Regex.Match(content, regexCI, options).Length > 0);


			if (containsTabs)
			{
				queryStringItems.Add("tabs=1");
			}
			if (containsVideo)
			{
				queryStringItems.Add("vid=1");
			}
			if (containsImages || containsUrlsToBeExpanded || containsHtmlContent)
			{
				queryStringItems.Add("hs=1");
			}
			else
			{
				queryStringItems.Add("hs=0");
			}
			if (queryStringItems.Count > 0)
				scriptQueryString = "?" + string.Join("&", queryStringItems.ToArray());

			string appPath = MetaPostServices.GetAppPath();
			_expanderScript = "<script type=\"text/javascript\" src=\"" + appPath + "/DesktopModules/itcMetaPost/js/ca0c21fbdc85f6a1597417732d450607.ashx" + scriptQueryString + "\"></script>";
			
			return (containsImages || containsUrlsToBeExpanded || containsHtmlContent || containsTabs);
		}
		# endregion

	}

	# region Structs
	public struct BlogInfoStruct
	{
		public string blogid;
		public string url;
		public string blogName;
	}


	// TODO: following attribute is a temporary workaround
	[XmlRpcMissingMapping(MappingAction.Ignore)]
	public struct Enclosure
	{
		public int length;
		public string type;
		public string url;
	}

	// TODO: following attribute is a temporary workaround
	[XmlRpcMissingMapping(MappingAction.Ignore)]
	public struct Source
	{
		public string name;
		public string url;
	}

	[XmlRpcMissingMapping(MappingAction.Ignore)]
	public struct Post
	{

		[XmlRpcMember("pubDate", Description = "The date to publish the blog entry.")]
		public DateTime pubDate;
		[XmlRpcMember("date_created_gmt", Description = "The GMT DateTime value when the blog entry was created.")]
		public DateTime date_created_gmt;
		[XmlRpcMissingMapping(MappingAction.Error)]
		[XmlRpcMember(Description = "Required when posting.")]
		public DateTime dateCreated;
		[XmlRpcMissingMapping(MappingAction.Error)]
		[XmlRpcMember(Description = "Required when posting.")]
		public string description;
		[XmlRpcMissingMapping(MappingAction.Error)]
		[XmlRpcMember(Description = "Required when posting.")]
		public string title;
		[XmlRpcMember("categories", Description = "Contains Categories for the post.")]
		public string[] categories;
		[XmlRpcMember("mt_keywords", Description = "List of Keywords for the post.")]
		public string mt_keywords;
		[XmlRpcMember("mt_allow_comments", Description = "Determines if comments will be allowed for this post.  (0 = none, 1 = open, 2=closed)")]
		public int? mt_allow_comments;

		[XmlRpcMember("mt_allow_pings",
			Description = "Determines if comments will be allowed for this post.  (0 = closed, 1 = open) ")]
		public int mt_allow_pings;
		public Enclosure enclosure;
		public string link;
		public string permalink;
		[XmlRpcMember(
		  Description = "Not required when posting. Depending on server may "
		  + "be either string or integer. "
		  + "Use Convert.ToInt32(postid) to treat as integer or "
		  + "Convert.ToString(postid) to treat as string")]
		public object postid;

		///// <summary>
		///// Not used by WLW.  Ignore for now.
		///// </summary>
		//public Source source;
		///// <summary>
		///// Not used by WLW.  Ignore for now.
		///// </summary>
		//public string userid;

		/// <summary>
		/// Used to track an SEO friendly description of the post, usually shorter than
		/// the Title and formatted for SEO presentation (eg. Using-Trackbacks-1).
		/// Only utilized if the supportsSlug entity is set to yes in the manifest file.
		/// </summary>
		public string wp_slug;
		/// <summary>
		/// Only utilized if the supportsPassword entity is set to yes in the manifest file.
		/// </summary>
		public string wp_password;
		/// <summary>
		/// Only utilized if the supportsPageParent entity is set to yes in the manifest file.
		/// </summary>
		[XmlRpcMember("wp_page_parent_id", Description = "Id for the parent of the page.")]
		public string wp_page_parent_id;
		/// <summary>
		/// Only utilized if the supportsPageOrder entity is set to yes in the manifest file.
		/// </summary>
		public string wp_page_order;		/// <summary>
		/// Only utilized if the supportsAuthor entity is set to yes in the manifest file.
		/// </summary>
		public string wp_author_id;		/// <summary>
		/// Only utilized if the supportsExcerpt entity is set to yes in the manifest file.
		/// </summary>
		public string mt_excerpt;		/// <summary>
		/// Only utilized if the supportsExtendedEntries entity is set to yes in the manifest file.
		/// </summary>
		public string mt_text_more;
		/// <summary>
		/// Used to manage trackback URLs.  Will only be populated if 
		/// supportsTrackbacks is set to yes in the manifest file.
		/// </summary>
		public string[] mt_tb_ping_urls;
		/// <summary>
		/// Used to track the status of the page.  This is a WordPress property.
		/// </summary>
		public string page_status;
	}

	public struct Page
	{
		[XmlRpcMember("page_id", Description = "Id for the page.")]
		public object page_id;
		[XmlRpcMember("page_title", Description = "Title of the page.")]
		public string page_title;
		[XmlRpcMember("page_parent_id", Description = "Id for the parent of the page.")]
		public string page_parent_id;
		[XmlRpcMember("DateCreated", Description = "Creation date of the page.")]
		public DateTime dateCreated;
	}

	public struct CategoryInfo
	{
		public string categoryId;
		public string parentId;
		public string description;
		public string categoryName;
		public string htmlUrl;
		public string rssUrl;
	}

	[XmlRpcMissingMapping(MappingAction.Ignore)]
	public struct PostedCategory
	{
		public string categoryId;
		public string categoryName;
		public bool isPrimary;
	}

	public struct MetaWebLogCategoryInfo
	{
		public string description;
		public string htmlUrl;
		public string rssUrl;
	}

	//public struct WPCategoryInfo
	//{
	//    public int categoryId;
	//    public int ParentId;
	//    public string description;
	//    public string categoryName;
	//    public string htmlUrl;
	//    public string rssUrl;
	//}

	//public struct Category
	//{
	//    public string categoryId;
	//    public string categoryName;
	//}

    [StructLayout(LayoutKind.Sequential), XmlRpcMissingMapping(MappingAction.Ignore)]
    public struct NewCategory
	{
		public string name;
		public string slug;
		public int parent_id;
		public string description;
	}

	public struct mediaObject
	{
		public string name;
		public string type;
		public Byte[] bits;
	}

	public struct mediaObjectInfo
	{
		public string url;
	}

	public interface IMetaWebLog
	{
		[XmlRpcMethod("metaWeblog.editPost", Description = "Updates and existing post to a designated blog "
		  + "using the metaWeblog API. Returns true if completed.")]
		bool editPost(
		 string postid,
		 string username,
		 string password,
		 Post post,
		 bool publish);

		[XmlRpcMethod("metaWeblog.getCategories",
		  Description = "Retrieves a list of valid Categories for a post "
		  + "using the metaWeblog API. Returns the metaWeblog Categories "
		  + "struct collection.")]
		MetaWebLogCategoryInfo[] getCategories(
		  string blogid,
		  string username,
		  string password);

		[XmlRpcMethod("metaWeblog.getPost",
		  Description = "Retrieves an existing post using the metaWeblog "
		  + "API. Returns the metaWeblog struct.")]
		Post getPost(
		  string postid,
		  string username,
		  string password);

		[XmlRpcMethod("metaWeblog.getRecentPosts",
		  Description = "Retrieves a list of the most recent existing post "
		  + "using the metaWeblog API. Returns the metaWeblog struct collection.")]
		Post[] getRecentPosts(
		  string blogid,
		  string username,
		  string password,
		  int numberOfPosts);

		[XmlRpcMethod("metaWeblog.newPost",
		  Description = "Makes a new post to a designated blog using the "
		  + "metaWeblog API. Returns postid as a string.")]
		string newPost(
		  string blogid,
		  string username,
		  string password,
		  Post post,
		  bool publish);

		[XmlRpcMethod("metaWeblog.newMediaObject",
		  Description = "Uploads an image, movie, song, or other media "
		  + "using the metaWeblog API. Returns the metaObject struct.")]
		mediaObjectInfo newMediaObject(object blogid, string username, string password, mediaObject mediaobject);

		#region BloggerAPI Members


		[XmlRpcMethod("blogger.deletePost",
			 Description = "Deletes a post.")]
		[return: XmlRpcReturnValue(Description = "Always returns true.")]
		bool deletePost(
			string appKey,
			string postid,
			string username,
			string password,
			[XmlRpcParameter(
				 Description = "Where applicable, this specifies whether the blog "
				 + "should be republished after the post has been deleted.")]
		  bool publish);

		[XmlRpcMethod("blogger.getUsersBlogs",
			 Description = "Returns information on all the blogs a given user "
			 + "is a member.")]
		BlogInfoStruct[] getUsersBlogs(
			string appKey,
			string username,
			string password);

		#endregion

		# region wp API Members

		[XmlRpcMethod("wp.getCategories",
				  Description = "Retrieves a list of valid Categories for a post "
				  + "using the WordPress API. The advantage of using the WordPress API "
				  + "is support for hierarchical Categories.")]
		CategoryInfo[] getWPCategories(
		  string blog_id,
		  string username,
		  string password);

		[XmlRpcMethod("wp.newCategory",
		  Description = "Adds a new category using the WordPress API. "
		  + "The advantage of using the WordPress API "
		  + "is support for hierarchical Categories.  Should return the new cateogry ID.")]
		int newCategory(
		  string blog_id,
		  string username,
		  string password,
		  NewCategory category);

		[XmlRpcMethod("wp.newPage",
		  Description = "Adds a new page using the WordPress API. "
		  + "This API is useful for applications which support multiple pages associated "
		  + "with a single entity.  Returns the new page ID.")]
		int newPage(
		  string blog_id,
		  string username,
		  string password,
		  Post content,
		  bool publish);

		[XmlRpcMethod("wp.editPage",
				  Description = "Edits an existing page using the WordPress API. "
				  + "This API is useful for applications which support multiple pages associated "
				  + "with a single entity.  Returns a boolean indicating success of edit.")]
		bool editPage(
		  string blog_id,
		  string page_id,
		  string username,
		  string password,
		  Post content,
		  bool publish);

		[XmlRpcMethod("wp.getPage",
		  Description = "Retrieves an existing page using the WordPress API. "
		  + "This API is useful for applications which support multiple pages associated "
		  + "with a single entity.  Returns a Post struct.")]
		Post getPage(
		  string blog_id,
		  string page_id,
		  string username,
		  string password);

		[XmlRpcMethod("wp.getPages",
		  Description = "Retrieves an array of existing pages using the WordPress API. "
		  + "This API is useful for applications which support multiple pages associated "
		  + "with a single entity.  Returns an array of the Post struct.")]
		Post[] getPages(
		  string blog_id,
		  string username,
		  string password,
		  int numberOfItems);

		[XmlRpcMethod("wp.getPageList",
		  Description = "Retrieves an array of existing pages using the WordPress API "
		 + "retrieving just the minimum details. Returns an array of the Post struct with "
		 + "only the following fields set: page_id, page_title, page_parent_id, DateCreated")]
		Page[] getPageList(
		  string blog_id,
		  string username,
		  string password);

		[XmlRpcMethod("wp.deletePage",
		  Description = "Deletes an existing page using the WordPress API "
		 + "Returns a bool indicating the success or failure of the delete operation.")]
		bool deletePage(
		  string blog_id,
		  string username,
		  string password,
		  string page_id);


		# endregion


		# region mt API Members

		// Added this to support the MovableType API because of an
		// issue introduced in the June CTP of WLW where the mt APIs were being
		// called for category management.
		[XmlRpcMethod("mt.setPostCategories",
		          Description = "Set post categories.  Implemented only to work"
		            + "around an issue in the June 2008 CTP of WLW.")]
		bool setPostCategories(
		  string postid,
		  string username,
		  string password,
		  PostedCategory[] categories);

		// Started adding this to support the MovableType API because of an
		// issue introduced in the June CTP of WLW where the mt APIs were being
		// called for category management.
		[XmlRpcMethod("mt.getPostCategories",
				  Description = "Get post categories.  Implemented only to work"
					+ "around an issue in the June 2008 CTP of WLW.")]
		PostedCategory[] getPostCategories(
		  string postid,
		  string username,
		  string password);
		# endregion

	}

	# endregion

    # endregion 
}


